iPhone - Multithreading in Tableview - feasible approach?
I have been tracking a nas开发者_JAVA百科ty bug recently of which I knew it was occurring due to my multithreaded approach (I add the crashreport at the end).
In my App I load a UITableView and fill it with data I store using Coredata. This data in turn I retrieve from a webservice, thus it might take some time, depending on my connection.
Anyway, I have managed to track it down a little and I know the issue is because an array is empty though I thought it shouldn't be. Then, when
numberOfRowsInSection:(NSInteger)section
is called the program crashes - but only sometimes!!!
I was wondering how to debug this properly and my approach was to call
reloadData
on the tableView after the view finishes loading. But appearantly
- (void)viewDidLoad
but of course this didn't help. Is the correct way here to create another thread and have it check repeatadly whether the data is ready and prepared? Any suggestions/opinions?
2011-01-19 21:50:49.605 myApp[2017:307] *** Terminating app due to uncaught exception
'NSRangeException', reason: '*** -[NSMutableArray objectAtIndex:]: index 0 beyond bounds for empty array'
*** Call stack at first throw:
(
0 CoreFoundation 0x314d0987 __exceptionPreprocess + 114
1 libobjc.A.dylib 0x319a149d objc_exception_throw + 24
2 CoreFoundation 0x31462795 -[__NSArrayM objectAtIndex:] + 184
3 myApp 0x000092f7 -[MyTableViewController tableView:numberOfRowsInSection:] + 106
4 UIKit 0x33902bcf -[UISectionRowData refreshWithSection:tableView:tableViewRowData:] + 1338
5 UIKit 0x33903529 -[UITableViewRowData(UITableViewRowDataPrivate) _ensureSectionOffsetIsValidForSection:] + 120
6 UIKit 0x33902645 -[UITableViewRowData numberOfRows] + 96
7 UIKit 0x3390207b -[UITableView noteNumberOfRowsChanged] + 82
8 UIKit 0x33901bff -[UITableView reloadData] + 582
9 UIKit 0x33904a0b -[UITableView _reloadDataIfNeeded] + 50
10 UIKit 0x33904e63 -[UITableView layoutSubviews] + 18
11 UIKit 0x338b10cf -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 26
12 CoreFoundation 0x3146ebbf -[NSObject(NSObject) performSelector:withObject:] + 22
13 QuartzCore 0x30a6c685 -[CALayer layoutSublayers] + 120
14 QuartzCore 0x30a6c43d CALayerLayoutIfNeeded + 184
15 QuartzCore 0x30a6656d _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 212
16 QuartzCore 0x30a66383 _ZN2CA11Transaction6commitEv + 190
17 QuartzCore 0x30a70e4f _ZN2CA11Transaction5flushEv + 46
18 QuartzCore 0x30a6db75 +[CATransaction flush] + 24
19 UIKit 0x338e803f -[UIApplication _reportAppLaunchFinished] + 30
20 UIKit 0x338d6317 -[UIApplication _runWithURL:payload:launchOrientation:statusBarStyle:statusBarHidden:] + 462
21 UIKit 0x338a248b -[UIApplication handleEvent:withNewEvent:] + 1114
22 UIKit 0x338a1ec9 -[UIApplication sendEvent:] + 44
23 UIKit 0x338a1907 _UIApplicationHandleEvent + 5090
24 GraphicsServices 0x35d66f03 PurpleEventCallback + 666
25 CoreFoundation 0x314656ff __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 26
26 CoreFoundation 0x314656c3 __CFRunLoopDoSource1 + 166
27 CoreFoundation 0x31457f7d __CFRunLoopRun + 520
28 CoreFoundation 0x31457c87 CFRunLoopRunSpecific + 230
29 CoreFoundation 0x31457b8f CFRunLoopRunInMode + 58
30 UIKit 0x338d5309 -[UIApplication _run] + 380
31 UIKit 0x338d2e93 UIApplicationMain + 670
32 myApp 0x000029bf main + 70
33 myApp 0x00002974 start + 40
)
terminate called after throwing an instance of 'NSException'
numberOfRows looks like:
- (NSInteger)tableView:(UITableView *)aTableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
NSLog(@"number of rows in section");
if (section == mySection) {
return [[articleArrays objectAtIndex:section] count];
} else {
return 0;
}
}
From what you describe here your process that performs the download, or more precisely an event callback should be the only place calling reload data. calling it after adding new data to the table dataSet.
returning a 0 in numberOfRowsInSection does not cause a crash but if you are trying to get a nested array when the count is 0, this will crash. Post your numberOfRowsInSection
method for us to gawk at.
EDIT:
Sounds/Looks like background thread is mutating the array while its trying to do an object count.
user210504 is right in saying that synchronising the array is likely stop this.
Typically you would sync writes and therefore preventing numberOfRowsInSection
from counting if your background thread is mutating it. eg:
-(void)downloadFinished{
NSMutableArray * array = [[NSArray alloc] init];//t
id * obj;//obj is your row data object.
@synchronized(ar)
{
[array addObject:obj];//ar is your dataSet
}
[array release];
}
One thing that you can do is to encapsulate accesses to the Mutable Array in @synchronized block. This way you will be sure that you are not accessing the datastructure half way through. And then I also agree with what Luke just mentioned.
精彩评论