6

我正在开发一个 iPad 应用程序,目前正在努力寻找最佳的多线程方法。让我用一个简化的例子来说明这一点:
我有一个包含 2 个子视图的视图、一个目录选择器和一个画廊,其中包含所选目录中所有图像的缩略图。由于“下载”和生成这些缩略图可能需要很长时间,所以我需要多线程,这样视图的交互和更新就不会被阻塞。

这是我已经尝试过的:
[self performSelectorInBackground:@selector(displayThumbnails:) withObject:currentFolder];
这很好用,因为用户交互没有被阻止,但是当用户在第一个文件夹仍在加载时点击另一个文件夹时,它会惨遭失败。两个线程试图访问相同的视图和变量,这会导致彼此之间的正确执行混乱。当用户点击另一个文件夹时,displayThumbnails当前加载的文件夹应该被中止。我没有找到任何方法来做到这一点..

NSThreads
我尝试了这个,但遇到了与第一种方法几乎相同的问题,我没有找到一种(简单的)方法来取消正在进行的方法。(是的,我知道[aThread cancel]但没有找到“恢复”线程的方法)。也许我应该NSThread继承并实现我自己的 isRunning 等方法?但是没有更好的方法或我忽略的第三个(甚至第四个和第五个)选项吗?

我认为这是一个相当简单的示例,并且我认为没有子类化可能有更好的解决方案NSThread。那么,你会怎么做?请发表您的意见!

4

3 回答 3

6

NSOperationQueue应该可以很好地完成这项任务。

另一种选择是普通的GCD,但是,如果您从未使用过它,则 NSOperationQueue 可能是更好的选择,因为它几乎会自动引导您以“正确的方式”实现事物,具有明显的取消方式等。

于 2012-07-15T22:18:08.353 回答
5

您想使用 Concurrent NSOperations 在后台下载和处理图像。这些将由 NSOperationsQueue 管理。本质上,这些操作将被配置为每次操作获取一个图像,对其进行处理,将其保存在文件系统中,然后在主线程中向主应用程序发送消息,告知图像可用。

您可以查看 github 上的几个项目,这些项目展示了如何执行此操作 - 只需使用“Concurrent”或“NSOperation”搜索 github。

iOS 有一个非常好的工具来做后台工作。Grand Central Dispatch (GCD) 和 Blocks,但它们不允许您使用委托回调来拥有对象 - 因此是 NSOperation。

因此,您需要阅读有关块、GCD 的内容,然后查看一些开源 Concurrent NSOperations 代码。使用 Concurrent NSOperations 并不像使用块那么简单。

于 2012-07-15T22:19:02.987 回答
0

如果我有这个问题,我可能会采用这样的方法:

  • 将加载图像并导致主线程显示结果的单个线程(我不喜欢让线程与 GUI 对象混淆)

  • 当请求一个新目录时......好吧,这取决于你想如何管理事物。基本上,一个标准的队列结构(条件变量和数组)可以用于主线程,通过传递路径名来告诉线程“将需要这个目录”;即使在加载图像时(例如在每个图像之后),线程也会检查队列,并在出现时切换到新目录

  • 您可以创建一个目录读取器对象来保存所有状态,并将路径索引的内容存储到字典中。当请求一个新目录时,首先检查该字典,如果该目录没有,则只创建一个新对象。这样,部分加载的目录将一直存在,直到再次需要它们,并且可以继续加载,而不必从头开始。

线程的伪代码:

while (forever)
   new element = nil
   if we have an active directory loader
       tell directory loader to load one image
       if false then make directory loader inactive
       lock queue condition
       if queue has elements
          new element = retrieve LAST element (we aren't interested in the others)
          empty queue
          unlock with status "empty"
       else
          unlock queue
   else
       lock queue on condition "has elements"
       new element = retrieve last element
       empty queue
       unlock with status "empty"
   if new element != nil
       if directory loader for new path does not exist
          setup new directory loader for new path
          store in dictionary
          make it the "active" one
       else
          make the current one the "active"

至于目录加载器,它可能看起来像这样:

read one image:
   if there are still images to read:
       read, process and store one
       return true
   else
       performSelectorOnMainThread with an "update GUI" method and the image list as parameter
       return false;

这只是一个速写;线程中有一些代码重复,我编写它的方式只会在读取所有图像后更新 GUI,而不是在我们读取它们时显示它们。您必须复制当前图像列表,或者如果您想这样做,则添加同步。

于 2012-07-15T22:32:24.890 回答