我有一个 Cocoa 应用程序 (Mac OS X SDK 10.7),它正在通过 Grand Central Dispatch (GCD) 执行一些进程。这些进程正在以我认为线程安全的方式操作一些核心数据 NSManagedObjects(非基于文档的)(创建一个新的 managedObjectContext 用于该线程)。
问题 我遇到的问题是当调度队列仍在运行时用户试图退出应用程序。
NSApplication 委托(delegate)在实际退出之前被调用。
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
我收到错误“无法合并更改”。这在某种程度上是意料之中的,因为仍然有一些操作正在通过不同的 managedObjectContext 执行。然后我看到了来自核心数据应用程序生成的模板的 NSAlert。
在Threading Programming Guide有一个名为“退出时注意线程行为”的部分暗示使用 replyToApplicationShouldTerminate:方法。我在实现这个时遇到了一些麻烦。
我希望 是我的应用程序完成对排队项目的处理,然后终止而不向用户显示错误消息。更新 View 或使用工作表让用户知道应用正在执行某些操作并将在操作完成时终止也会很有帮助。
我将在何处以及如何实现此行为?
解决方案: 所以我在这里遇到了几个不同的问题。
我有 block 访问
dispatch_queue
中的核心数据,阻止我的应用程序正常终止。当我尝试向 dispatch_queue 添加新项目时,dispatch_queue 的新实例在新线程上启动。
我为解决这个问题所做的是在我的 AppDelegate
中使用 NSNotificationCenter
(其中 (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
被调用。在 Core Data 生成的模板代码中添加以下内容:
// Customize this code block to include application-specific recovery steps.
if (error) {
// Do something here to add queue item in AppController
[[NSNotificationCenter defaultCenter] postNotificationName:@"TerminateApplicationFromQueue" object:self];
return NSTerminateLater;
}
然后在 AppController
中为通知添加一个观察者(我将其添加到 awakeFromNib
):
- (void)awakeFromNib {
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:@selector(terminateApplicationFromQueue:) name:@"TerminateApplicationFromQueue" object:nil];
// Set initial state of struct that dispatch_queue checks to see if it should terminate the application.
appTerminating.isAppTerminating = NO;
appTerminating.isTerminatingNow = NO;
}
我还创建了一个 struct
,可以检查它以确定用户是否想要终止应用程序。 (我在上面的 awakeFromNib
中设置了结构的初始状态)。将 struct
放在 @synthesize
语句之后:
struct {
bool isAppTerminating;
bool isTerminatingNow;
} appTerminating;
现在是长时间运行的 dispatch_queue
,它阻止应用正常终止。当我最初创建此 dispatch_queue
时,使用了一个 for 循环来添加需要更新的项目。执行此 for 循环后,我添加了另一个队列项,它将检查 struct
以查看应用程序是否应终止:
// Additional queue item block to check if app should terminate and then update struct to terminate if required.
dispatch_group_async(refreshGroup, trackingQueue, ^{
NSLog(@"check if app should terminate");
if (appTerminating.isAppTerminating) {
NSLog(@"app is terminating");
appTerminating.isTerminatingNow = YES;
}
});
dispatch_release(refreshGroup);
以及收到通知时调用的方法:
- (void)terminateApplicationFromQueue:(NSNotification *)notification {
// Struct to check against at end of dispatch_queue to see if it should shutdown.
if (!appTerminating.isAppTerminating) {
appTerminating.isAppTerminating = YES;
dispatch_queue_t terminateQueue = dispatch_queue_create("com.example.appname.terminate", DISPATCH_QUEUE_SERIAL); // or NULL
dispatch_group_t terminateGroup = dispatch_group_create();
dispatch_group_async(terminateGroup, terminateQueue, ^{
NSLog(@"termination queued until after operation is complete");
while (!appTerminating.isTerminatingNow) {
// add a little delay before checking termination status again
[NSThread sleepForTimeInterval:0.5];
}
NSLog(@"terminate now");
[NSApp replyToApplicationShouldTerminate:YES];
});
dispatch_release(terminateGroup);
}
}
最佳答案
我自己还没有处理过这个问题,但从我对文档的阅读来看,看起来你应该做的是:
- 从
applicationShouldTerminate:
返回NSTerminateLater
。这让系统知道您的应用尚未准备好终止目前,但很快就会终止。 - 在您的调度队列中加入一个“最终” block 。 (您需要确保在此之后其他 block 没有入队。此 block 将在执行所有其他工作后运行。注意队列必须是串行的——而不是并发队列之一)才能正常工作.) “最终” block 应该执行
[NSApp replyToApplicationShouldTerminate:YES];
,这将完成正常的终止过程。
没有任何直接的方法可以查明 GCD 队列是否仍在工作。您唯一可以做的(据我所知)来处理这个问题是将所有 block 放入 dispatch group 中。 ,然后在 applicationShouldTerminate:
中等待组(使用 dispatch_group_wait()
。
关于objective-c - 使用 Core Data 和 Grand Central Dispatch (GCD) 优雅终止 NSApplication,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10081123/