I am creating a dictionary application for iPhone that gives result while the users are typing. I use threads (NSThread) to update the UITableView so that the main thread is not blocked.
However, a crash happens when the UITableView asks the data source for the number of rows ( tableView:numberOfRowsInSection:) and I return, say, 10. Then it asks the data source for cells 0-9 (tableView:cellForRowAtIndexPath:). But by the time it asks for cell 7, the data source has already changed, and it now has only 5 rows, thus causing a crash.
Here is how I solve the problem:
I create a NSLock in the init method.
And here is what the data source looks like:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [results count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
[lock lock];
if (indexPath.row < [results count]) {
cell.textLabel.text = [results objectAtIndex:indexPath.row];
}
[lock unlock];
return cell;
}
And here is the code that I use to update the table:
[tableView performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO];
It solves the crash problem completely. However, I think that it might not be efficient because the data source has to lock/unlock every time it is asked for a cell. And the case that I mentioned above doesn't happen that often. Does anyone have a better idea of how to solve this problem efficiently?
Thank you very much!
Do not try to update the UI from a background thread. It will not work.
Why would you use a separate thread for this? How long does the search take? 0.1 seconds? How does that compare to the time taken by the user to stop typing and look at the screen?
Don't over-complicate things! (I will take this back if your search takes more than 0.7 seconds and cannot be optimized ;-)
Just so there's a useful answer here, here's my answer to another, similar, question.
Without knowing more about your app, I think one of the better solutions would be to keep the array on the main thread and dispatch back to it whenever another thread needs to make a change. Like this:
dispatch_async(dispatch_get_main_queue(), ^{
[array addObject:object];
[tableView reloadData];
});
Of course, you can get much more complex with the dispatch API, but it does handle locking and everything for you. Definitely more elegant than using an NSLock. It does only work on iOS 4 or later though.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With