Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can initializing NSAttributedString in tableView:heightForRowAtIndexPath: be implicitly recursive?

I am having a very strange problem when calculating the height of a UITableViewCell.

It seems like if I instantiate an NSAttributedString with NSData containing some HTML, a layout cycle is forced on the present view, which ends up calling tableView:heightForRowAtIndexPath: again. And also, heights for all of the other rows are requested in this pass. Luckily, the inner loop of the row height request doesn't have another set of recursive calls within it.

Here is the stacktrace: (note frames #0 and #25)

#0  0x0024422c in -[FeedVC tableView:heightForRowAtIndexPath:] at /Users/me/project/Classes/controllers/FeedVC.m:371
#1  0x024516cc in __66-[UISectionRowData refreshWithSection:tableView:tableViewRowData:]_block_invoke ()
#2  0x02450fd9 in -[UISectionRowData refreshWithSection:tableView:tableViewRowData:] ()
#3  0x02455e2f in -[UITableViewRowData rectForFooterInSection:heightCanBeGuessed:] ()
#4  0x02455f40 in -[UITableViewRowData heightForTable] ()
#5  0x022c68c2 in -[UITableView _adjustExtraSeparators] ()
#6  0x022da6d3 in -[UITableView layoutSubviews] ()
#7  0x0225a964 in -[UIView(CALayerDelegate) layoutSublayersOfLayer:] ()
#8  0x040d582b in -[NSObject performSelector:withObject:] ()
#9  0x01f7145a in -[CALayer layoutSublayers] ()
#10 0x01f65244 in CA::Layer::layout_if_needed(CA::Transaction*) ()
#11 0x01f650b0 in CA::Layer::layout_and_display_if_needed(CA::Transaction*) ()
#12 0x01ecb7fa in CA::Context::commit_transaction(CA::Transaction*) ()
#13 0x01eccb85 in CA::Transaction::commit() ()
#14 0x01ecd258 in CA::Transaction::observer_callback(__CFRunLoopObserver*, unsigned long, void*) ()
#15 0x03b7836e in __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ ()
#16 0x03b782bf in __CFRunLoopDoObservers ()
#17 0x03b56254 in __CFRunLoopRun ()
#18 0x03b559d3 in CFRunLoopRunSpecific ()
#19 0x03b557eb in CFRunLoopRunInMode ()
#20 0x0a2518ce in -[NSHTMLReader _loadUsingWebKit] ()
#21 0x0a25423e in -[NSHTMLReader _load] ()
#22 0x0a254ef7 in -[NSHTMLReader attributedString] ()
#23 0x0a1e7b42 in _NSReadAttributedStringFromURLOrData ()
#24 0x0a1e6577 in -[NSAttributedString(NSAttributedStringKitAdditions) initWithData:options:documentAttributes:error:] ()
#25 0x0024434a in -[FeedVC tableView:heightForRowAtIndexPath:] at /Users/me/project/Classes/controllers/FeedVC.m:374
#26 0x024516cc in __66-[UISectionRowData refreshWithSection:tableView:tableViewRowData:]_block_invoke ()
#27 0x02450fd9 in -[UISectionRowData refreshWithSection:tableView:tableViewRowData:] ()
#28 0x0245423d in -[UITableViewRowData numberOfRows] ()
#29 0x022d1df2 in -[UITableView noteNumberOfRowsChanged] ()
#30 0x022d17a5 in -[UITableView reloadData] ()
#31 0x1281be61 in -[UITableViewAccessibility(Accessibility) reloadData] ()
#32 0x0024236b in -[FeedVC addFeedItemsWhenFinishedLoading:] at /Users/me/project/Classes/controllers/FeedVC.m:168
#33 0x00242cba in __21-[FeedVC execRequest]_block_invoke99 at /Users/me/project/Classes/controllers/FeedVC.m:210
#34 0x0011b144 in -[GRApiUpdatesFriends didParseXMLObtainingResource:error:] at /Users/me/project/Classes/api/GRApiUpdatesFriends.m:46
#35 0x002113c1 in -[GRApiRequest didReceiveResponse:HTTPStatusCode:] at /Users/me/project/Classes/GRApiRequest.m:242
#36 0x0018b96f in __40-[GRApiFetcherAF exec:withMethod:retry:]_block_invoke at /Users/me/project/Classes/GRApiFetcherAF.m:138
#37 0x0057915b in __64-[AFHTTPRequestOperation setCompletionBlockWithSuccess:failure:]_block_invoke46 at /Users/me/project/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperation.m:137
#38 0x0436c7b8 in _dispatch_call_block_and_release ()
#39 0x043814d0 in _dispatch_client_callout ()
#40 0x0436f726 in _dispatch_main_queue_callback_4CF ()
#41 0x03c1543e in __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ ()
#42 0x03b565cb in __CFRunLoopRun ()
#43 0x03b559d3 in CFRunLoopRunSpecific ()
#44 0x03b557eb in CFRunLoopRunInMode ()
#45 0x053fe5ee in GSEventRunModal ()
#46 0x053fe42b in GSEventRun ()
#47 0x021ebf9b in UIApplicationMain ()
#48 0x0000eec6 in main at /Users/me/project/OtherSources/main.m:12

I am creating the attributed string as follows:

- (CGFloat)tableView:(UITableView*)tv heightForRowAtIndexPath:(NSIndexPath *)ip {
    NSLog(@"requested height row for index path: %d", ip.row);
    NSData *html_data = [@" " dataUsingEncoding:NSUTF8StringEncoding];
    NSDictionary *out_dict, *options = @{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType};
    NSError *err;
    NSAttributedString *attr_str = [[NSAttributedString alloc] initWithData:html_data options:options documentAttributes:&out_dict error:&err];
}

The recursion happens on the initWithData:

The blank string is just a sample. In our app, there is an actual HTML string.

Anyone have any idea why this might be happening?

I couldn't create a sample project to illustrate this because I can't seem to be able reproduce this in a simple UITableViewController project.

like image 759
jeffwong Avatar asked May 29 '14 05:05

jeffwong


2 Answers

Hard to say, but my guess is that the problem is #20. That seems to cause the code to reenter CFRunLoopRun which then triggers queued table events that weren't supposed to run until after reloadData was finished.

Suggested work-around: create your strings during initialization and store them in an NSArray so that they can be retrieved when needed by heightForRowAtIndexPath.

like image 116
user3386109 Avatar answered Nov 13 '22 00:11

user3386109


I've run into this same problem. It seems that initialising a NSAttributedString with html data runs an internal runloop while the html is being processed (by webkit, it seems..).

I've had a problem whereby setting an attributed string inside a table cell while it's scrolling sometimes causes a crash as the table continues scrolling before the attributed string has been initialised.

I solved it by constructing the NSAttributedString manually without using the html initialiser, as the formatting I was using wasn't too complicated.

like image 41
NigelG Avatar answered Nov 12 '22 23:11

NigelG