揭开NSOperation的神秘面纱:并发与非并发和异步模式

时间:2012-06-02 14:34:59

标签: objective-c asynchronous nsurlconnection nsoperation nsoperationqueue

是的,我知道。关于NSOperation世界有很多问题和答案,但我仍然有些疑惑。我试图用两部分问题来解释我的怀疑。他们彼此相关。

在SO帖nsoperationqueue-and-concurrent-vs-non-concurrent中, Darren 写了

  

“并发”操作本身是并发的;它不需要   NSOperationQueue为它创建一个线程。

但是稍微搜索一下,我发现NSOperation,即使它被声明为并发(通过覆盖isConcurrent方法,例如它返回YES),可以添加到NSOperationQueue。这是什么意思?如果我将一个并发NSOperation添加到队列中,那么幕后发生了什么?相反,如果我按原样使用并发操作(不将其添加到队列中)会发生什么?

Apple doc提供的说明很清楚:

  

...操作队列忽略返回的值   isConcurrent并始终从a调用操作的start方法   单独的线程。 ...一般来说,如果你总是   使用操作队列的操作,没有理由   他们并发。

然后,我真的对在NSOperation中使用异步模式感兴趣。我找到了 Dave Dribin concurrent operations)的好教程。我得到了他职位的整体意义。

您无法使用异步模式(例如,使用异步NSURLConnection请求),因为无法调用委托。当main完成后,操作将被删除。解决方案是覆盖start方法来控制操作生命周期......处理运行循环可能会很痛苦。

现在,试图了解他的帖子,我怀疑是否需要在主线程中运行start方法。

- (void)start
{
    if (![NSThread isMainThread])
    {
        [self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO];
        return;
    }

    // other code here...
}
  

在处理异步API时,我们可以开始异步   在启动时调用主线程并保持运行直到   它结束了。

你能解释一下为什么吗?

提前谢谢。

2 个答案:

答案 0 :(得分:11)

在我看来,isConcurrent的{​​{1}}属性容易引起混淆。它的确意味着“异步”。也就是说,当调用NSOperation时,无论操作是否已完成(异步),它是否会快速返回?或者直到操作完成(同步)才返回?

正如Apple的文档所述,当一个操作排队到-start时,这并不重要,因为队列在工作线程上调用它,无论如何。如果它是同步的,那么该工作线程将专门用于该操作,直到它完成。如果它是异步的,那么NSOperationQueue将在操作完成之前返回,并且工作线程可以继续进行其他工作。

问题是,异步-start方法如何确保操作的工作继续进行? 可能需要产生一个单独的线程才能完成工作,但这很愚蠢。最好让-start处理线程。

更有可能的是,它使用由外部事件驱动的运行循环源。异步模式中的NSOperationQueue就是那种东西。但是,在这种情况下,它必须确保它调度运行循环源的线程a)将继续存在,并且b)将运行其运行循环。 NSURLConnection的工作线程不能依赖它们。

同样,你可以为每个这样的操作创建自己的线程,特别是坚持并运行它的运行循环,但这是不必要的,再次,没有优势使得操作保持同步并排队

你已经知道的一个线程会坚持并运行它的运行循环是主线程。因此,通常最好在主线程的运行循环上安排运行循环源。唯一需要注意的是,为了响应触发运行循环源处理程序的外部事件,您不会在主线程上执行任何长时间运行的工作。因此,例如,当NSOperationQueue使用接收到的数据调用您的委托方法时,您不会对该数据进行昂贵的计算 - 或者,如果必须,则将该昂贵的计算移动到另一个线程。

另一种可能性,即中间立场,就是创建自己的一个线程作为许多异步操作的工作者。因此,不是使用主线程或每个操作的线程,而是使用单个线程,其工作只是停留在其运行循环中。所有异步操作都会在该线程的运行循环上安排自己。但是,这种方法并没有太多需要或优势。

答案 1 :(得分:1)

我认为Dave解决了他将启动方法分流到帖子中主线程的动机:

  

更新2009-09-13:自10.6起不再适用。启动方法   从10.6开始,总是在后台线程上调用。要正常工作   仅限主线程和依赖运行循环的异步API,   我们需要将我们的工作分流到主线程。更多关于这一点   后续帖子。