【问题标题】:Update UITableView using threads使用线程更新 UITableView
【发布时间】:2010-07-30 20:05:26
【问题描述】:

我正在为 iPhone 创建一个字典应用程序,它会在用户键入时给出结果。我使用线程(NSThread)来更新UITableView,这样主线程就不会被阻塞了。

但是,当 UITableView 向数据源询问行数 (tableView:numberOfRowsInSection:) 并且我返回 10 时发生崩溃。然后它向数据源询问单元格 0-9 (tableView:cellForRowAtIndexPath: )。但是当它请求单元格 7 时,数据源已经发生了变化,现在它只有 5 行,从而导致崩溃。

这是我解决问题的方法:

我在 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];

彻底解决了崩溃问题。但是,我认为它可能效率不高,因为数据源每次被要求提供单元格时都必须锁定/解锁。而我上面提到的情况并不经常发生。有谁知道如何有效地解决这个问题?

非常感谢!

【问题讨论】:

  • 你不应该在另一个线程中更新 UI。
  • 瑞克,我必须在另一个线程中搜索结果,因为搜索方法很慢,我不希望它阻塞主线程。您能否解释一下我如何在不使用其他线程的情况下实现这一目标?
  • 让它更快。你到底在搜索什么?!
  • 前缀搜索 200,000 条非唯一记录
  • 按第一个字母索引它们可以节省 2log26 的二进制搜索步骤。将第二个字母也编入索引,平均可以让您浏览 300 条记录。必须在 0.1 秒内完成。

标签: iphone cocoa-touch multithreading uitableview


【解决方案1】:

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

【讨论】:

  • 这就是为什么我使用 [tableView performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO];它可以工作(从某种意义上说,不再有崩溃。)但我想通过每次询问单元格时不使用锁定/解锁来使其工作得更快。
【解决方案2】:

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

不要让事情过于复杂! (如果您的搜索时间超过 0.7 秒且无法优化,我将收回此内容;-)

【讨论】:

  • 说实话大概需要0.2-0.3秒。 (原来使用 SQLite/FTS3 搜索大约需要 0.5 秒,所以我使用纯文本和二进制搜索对其进行了优化)尽管它很小,但结合更新表所需的时间,它仍然足够大,可以给出我感觉键盘很“粘”。
  • 所以您正在查看大约 100 万条记录...我将专注于优化数据查找,这也将有利于用户体验。一方面,如果用户键入“b”,则没有理由立即制作整个“b”列表。
  • 我用 SQLite 试过了,即使我使用“LIMIT 1”,它也没有任何区别。我猜这可能是因为 SQLite 先查找整个列表,然后对其进行排序(即使我没有使用任何“ORDER BY”),最后返回结果。这就是为什么我切换到二分查找,这样我就可以查找下界和上界的索引,然后根据需要查询结果。
  • 如果您需要速度,请发明您自己的专用数据结构。应该不会很辛苦。说类似struct tree { struct tree *letters[26]; char **entries; }。然后walk = walk-&gt;letters[*searchStr++] 直到您点击 NULL,然后查看条目。在启动时从 sqlite 加载它(在后台线程中;-)
  • 如果查询需要很长时间,比如通过 Internet 完成的查询,该怎么办?让我们回答这家伙最初的问题。
【解决方案3】:

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

在不了解您的应用程序的情况下,我认为更好的解决方案之一是将数组保留在主线程上,并在另一个线程需要进行更改时将其分派回它。像这样:

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

当然,您可以使用调度 API 变得更复杂,但它确实为您处理锁定和一切。绝对比使用 NSLock 更优雅。但它仅适用于 iOS 4 或更高版本。

【讨论】:

    【解决方案4】:

    您无法在后台线程中更新 UI。

    使用Grand Central Dispatch

    我在这里解释

    https://stackoverflow.com/a/15469885/1239426

    【讨论】:

      猜你喜欢
      • 2015-11-16
      • 1970-01-01
      • 2013-12-09
      • 1970-01-01
      • 1970-01-01
      • 2023-03-29
      • 1970-01-01
      • 1970-01-01
      • 2019-12-06
      相关资源
      最近更新 更多