2010-07-30 31 views
5

我正在为用户输入时给出结果的iPhone创建字典应用程序。我使用线程(NSThread)来更新UITableView,以便主线程不被阻塞。但是,当UITableView询问数据源的行数(tableView:numberOfRowsInSection :)并返回,比如说10时,就会发生崩溃。然后它询问数据源的单元格0-9(tableView:cellForRowAtIndexPath :)。但是当它要求单元7时,数据源已经改变了,现在它只有5行,从而导致崩溃。使用线程更新UITableView

这是我如何解决这个问题:

我在init()方法创建一个NSLock。

而且这里是数据源的样子:

- (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; 
} 

这里是我用来更新表的代码:

[tableView performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO]; 

它彻底解决了死机问题。不过,我认为它可能效率不高,因为数据源在每次询问单元时都必须锁定/解锁。而我上面提到的情况并不经常发生。有没有人有更好的想法如何有效地解决这个问题?

非常感谢!

+2

你不应该更新在另一个线程的UI。 – rickharrison 2010-07-30 20:06:45

+0

Rick,我必须在另一个线程中搜索结果,因为搜索方法非常慢,我不希望它阻塞主线程。你能解释一下如何在不使用其他线程的情况下实现这一目标吗? – ifvc 2010-07-30 20:14:52

+0

使它更快。你究竟在寻找什么?! – mvds 2010-07-30 20:17:51

回答

2

为什么你会为此使用一个单独的线程?搜索需要多长时间? 0.1秒?这与用户停止键入并查看屏幕所用的时间相比如何?

不要过分复杂的东西! (如果您的搜索时间超过0.7秒并且无法进行优化,我会将其退回;-)

+0

说实话,大概需要0.2-0.3秒。 (使用SQLite/FTS3的原始搜索大约需要0.5秒,所以我使用纯文本和二分搜索进行了优化)尽管它很小,但如果与更新表所需的时间相结合,它仍然足够大,我感觉像键盘是“粘性”。 – ifvc 2010-07-30 20:33:57

+0

因此,您正在查看大约1百万条记录...我会专注于优化数据查找,这也将有益于用户体验。首先,如果用户键入“b”,则没有理由立即制作整个“b”列表。 – mvds 2010-07-30 21:11:54

+0

我试过用SQLite,即使我使用“LIMIT 1”,它也没有什么区别。我想这可能是因为SQLite首先查找整个列表,然后对它进行排序(即使我没有使用任何“ORDER BY”),并最终返回结果。 这就是为什么我切换到二分搜索,所以我可以查找下边界和上边界的索引,然后根据需要查询结果。 – ifvc 2010-07-30 21:41:23

4

不要尝试从后台线程更新UI。不起作用。

+1

这就是为什么我使用[tableView performSelectorOnMainThread:@selector(reloadData)withObject:nil waitUntilDone:NO];并且它可以工作(从某种意义上说,没有崩溃)。但是,我希望通过每次询问单元时不使用锁定/解锁来更快地工作。 – ifvc 2010-07-30 20:42:48

1

这里有一个有用的答案,这里是我对另一个类似问题的回答。

如果不知道更多关于您的应用的信息,我认为更好的解决方案之一是将数组保存在主线程中,并在其他线程需要进行更改时将其分派给它。像这样:

dispatch_async(dispatch_get_main_queue(), ^{ 
       [array addObject:object]; 
       [tableView reloadData]; 
      }); 

当然,你可以使用dispatch API获得更复杂的结果,但它确实为你处理锁定和一切。绝对比使用NSLock更优雅。它只适用于iOS 4或更高版本。