使用并发时如何在退出时不崩溃应用程序

时间:2015-06-05 03:07:27

标签: objective-c macos swift cocoa nsoperation

我在我的应用中使用NSOperationQueueNSOperation的子类来生成大量数据,因此计算量非常大。

当用户processingQueue.cancelAllOperations()关闭应用程序时,会调用该应用程序。同样在我的NSOperation子类中,我覆盖了cancel(),让它将取消请求转发给执行实际繁重工作的类......

override func cancel() {
    AppDelegate.instance.cancelDataGeneration()
    super.cancel()
}

但这仍然不够。当我在数据生成过程中关闭应用程序时,它将在Xcode中崩溃。

如何防止崩溃(可能导致数据丢失)?是否可以让应用程序等待关闭,直到所有并发操作都被取消,以及如何完成(如果它甚至可能)?或者通常使用哪些其他方法来解决这个问题?

更新

经过更多调查后,我发现即使在cancel()中调用NSOperation之后,processingQueue.cancelAllOperations()子类上的applicationShouldTerminate也从未被调用过。所以我添加了一种手动调用取消的方法:

func cancelDataGeneration() {
    if let op = AppDelegate.instance._operation {
        op.cancel();
    }
}

我从applicationShouldTerminate内部调用此内容(因为applicationShouldTerminate早于applicationWillTerminate调用。有趣的是,由于我的AppDelegate是一个Singleton,我必须使用AppDelegate.instance._operation。我只检查_operation,当nil调用时,applicationShouldTerminate结果为NSOperation。知道为何会出现这种情况会很有趣。

在任何情况下,取消现在都能正常工作:当应用程序退出时,它将取消数据生成类并退出而不会崩溃...大多数情况下。但我仍然想知道为什么我的cancel()子类'当我使用processingQueue.cancelAllOperations()时,{{1}}没有被调用!

1 个答案:

答案 0 :(得分:1)

来自Apple's documentation

  

取消操作不会自动将其从队列中删除或停止当前正在执行的操作。对于排队和等待执行的操作,队列必须仍然尝试执行操作,然后才能识别它被取消并将其移动到完成状态。

我会阻止应用程序的主线程,直到NSOperationQueue完成所有工作。

  • 我先打电话给[NSOperationQueue cancelAllOperations]
  • 然后在'应用程序将终止'方法我会打电话 [NSOperationQueue waitUntilAllOperationsAreFinished]。这将确保在应用程序退出之前完成当前正在执行的块(所有其他排队任务将被取消)。

    现在,如果您对主线程blocking感到不舒服,直到当前正在执行的块完成,那么您需要检查一个标志(或者可以在该类上设置NSApplicationDelegate)哪个信号如果应用程序仍处于活动状态以便继续。如果要终止应用程序,然后阻止fall outvoluntarily,这是最干净的方法。

    大致如下所示。

    void ^(aBlock)(void) = ^void(void)
    {
        for(NSUInteger i = 0;i < 1000; i++)
        {
            // heavy processing code. Taking several iterations each second
            // At the start of each iteration, check for a flag, to see if to quit
            if(_applicationIsShuttingDown) break;
    
            // perform block operation
        }
    };
    

    并且您的类是NSApplicationDelegate并实现

    -applicationWillTerminate:(NSNotification *)aNotification
    {
        _applicationIsShuttingDown = YES;
    }