5

我正在设计一个 iPhone 应用程序。用户搜索一些东西。我们从网上获取数据。然后我们更新表。

伪代码将是

[DoThisAtbackground ^{
  LoadData ();
  [DoThisAtForeground ^{
    UpdateTableAndView();
  }];
}];

如果在第一次搜索完成之前用户搜索别的东西怎么办。

解决问题的行业标准方法是什么?

  1. 跟踪哪个线程仍在运行,仅在所有线程完成后才更新表?
  2. 每次线程完成时更新视图?

我们究竟是如何做到这一点的?

4

3 回答 3

2

我建议你看看iOS Human Interface Guidelines。Apple 认为所有应用程序的行为方式大致相同非常重要,因此他们编写了一份关于此类问题的详尽文档。

在指南中,有两件事与您的问题相关:

  • 让搜索快速而有价值:“如果可能,在用户输入时过滤远程数据。虽然过滤用户输入可以带来更好的搜索体验,但如果响应时间可能,请务必通知他们并让他们有机会选择退出将结果延迟一两秒以上。”
  • 反馈:“反馈承认人们的行为,并向他们保证正在进行处理。人们在操作控件时期望立即得到反馈,并且他们欣赏在长时间操作期间的状态更新。”

虽然这些指南当然有很多废话,但我认为以上几点实际上是一个好主意。作为用户,我希望搜索时会发生一些事情,并且当您在每次线程完成时更新视图时,用户将看到最快的响应。是的,这可能是用户不想要的结果,但有些事情正在发生!例如,以 iOS 中的 Safari 网络浏览器为例:Google 自动完成功能即使在您输入时也会显示结果,而不仅仅是在您输入完搜索查询时。

所以我认为最好选择第二个选项。

于 2012-09-09T12:34:28.703 回答
2

如果您正在对远程服务器执行 REST 数据请求,您可以随时取消请求并启动新请求,而无需更新表,这是一种可行的方法。有时间完成的请求将更新 UI,而其他请求则不会。例如使用 ASIHTTPRequest

- (void)serverPerformDataRequestWithQuery:(NSString *)query andDelegate:(__weak id <ServerDelegate)delegate {
  [currentRequest setFailedBlock:nil];
  [currentRequest cancel];
  currentRequest = [[ASIHTTPRequest alloc] initWithURL:kHOST];
  [currentRequest startAsynchronous];
}

如果您也需要本地 SQLite 数据库的答案,请告诉我,因为它要复杂得多。

于 2012-09-09T12:39:16.710 回答
0

您可以使用NSOperationQueue取消所有挂起的操作,但它仍然不会取消现有的操作。您仍然需要执行一些操作来取消现有操作......这也可以提前中止队列中的操作。

我通常更喜欢直接 GCD,除非在我的用例中有其他更适合NSOperationQueue.

此外,如果您的加载具有外部取消机制,您希望取消任何挂起的 I/O 操作。

如果操作是独立的,请考虑并发队列,因为它将允许较新的请求在其他请求被取消时同时执行。

此外,如果它们都是 I/O,请考虑是否可以使用dispatch_io而不是阻塞线程。正如蒙克所说,“你以后会感谢我的。”

考虑这样的事情:

- (void)userRequestedNewSearch:(SearchInfo*)searchInfo {
    // Assign this operation a new token, that uniquely identifies this operation.
    uint32_t token = [self nextOperationToken];

    // If your "loading" API has an external abort mechanism, you want to keep
    // track of the in-flight I/O so any existing I/O operations can be canceled
    // before dispatching new work.

    dispatch_async(myQueue, ^{
        // Try to load your data in small pieces, so you can exit as early as
        // possible.  If you have to do a monolithic load, that's OK, but this
        // block will not exit until that stops.  
        while (! loadIsComplete) {
            if ([self currentToken] != token) return;
            // Load some data, set loadIsComplete when loading completes
        }

        dispatch_async(dispatch_get_main_queue(), ^{
            // One last check before updating the UI...
            if ([self currentToken] != token) return;

            // Do your UI update operations
        });
    });

}

它将提前中止任何不是最后提交的操作。如果您使用过NSOperationQueue,您可以调用cancelAllOperations,但您仍然需要一种类似的机制来提前中止当前正在执行的机制。

于 2012-09-09T13:33:00.903 回答