Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Update UITableView using threads

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!

like image 379
ifvc Avatar asked Jul 30 '10 20:07

ifvc


3 Answers

Do not try to update the UI from a background thread. It will not work.

like image 199
Alex Reynolds Avatar answered Nov 12 '22 04:11

Alex Reynolds


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 ;-)

like image 38
mvds Avatar answered Nov 12 '22 02:11

mvds


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.

like image 1
Jon Shier Avatar answered Nov 12 '22 02:11

Jon Shier