views:

53

answers:

2

Hi guys,

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!

+1  A: 

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

mvds
To be honest, it takes about 0.2-0.3 seconds. (The original search using SQLite/FTS3 takes about 0.5 seconds, so I optimized it by using plain text and binary search) Even though it's small, when combined with the time it takes to update the table, it's still big enough that it can give me a feeling like the keyboard is "sticky."
ifvc
So you're looking at some 1m records... I would focus on optimizing the data lookup, it will benefit the user experience as well. For one thing, if a user types "b" there is no reason to cook up the entire "b" list immediately.
mvds
I tried that with SQLite and it made no difference, even if I use "LIMIT 1". I guess it might be because SQLite looks for the entire list first, then sorts it (even though I don't use any "ORDER BY"), and finally returns the result.That's why I switched to binary search, so I can look up for the indexes of the lowerbound and the upperbound, then query the result as needed.
ifvc
If you need speed, invent your own purpose-built data structure. Shouldn't be very hard. Say something like `struct tree { struct tree *letters[26]; char **entries; }`. Then `walk = walk->letters[*searchStr++]` until you hit NULL, then go through the entries. Load it from the sqlite at startup (in a background thread ;-)
mvds
+2  A: 

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

Alex Reynolds
That's why I use [tableView performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO]; and it works (in the sense that there is no crash anymore.) But I would like to make it work faster by not using lock/unlock every time a cell is asked.
ifvc