Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/objective-c/27.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/macos/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Objective c 使用核心数据和大中心调度(GCD)优雅地终止非应用程序_Objective C_Macos_Cocoa - Fatal编程技术网

Objective c 使用核心数据和大中心调度(GCD)优雅地终止非应用程序

Objective c 使用核心数据和大中心调度(GCD)优雅地终止非应用程序,objective-c,macos,cocoa,Objective C,Macos,Cocoa,我有一个Cocoa应用程序(Mac OS X SDK 10.7),它通过Grand Central Dispatch(GCD)执行一些进程。这些进程正在以我认为是线程安全的方式操作一些核心数据NSManagedObject(非基于文档的)(创建一个新的ManagedObject上下文以在该线程中使用) 我遇到的问题是,当调度队列仍在运行时,用户试图退出应用程序 在实际退出之前正在调用NSApplication委托 - (NSApplicationTerminateReply)applicatio

我有一个Cocoa应用程序(Mac OS X SDK 10.7),它通过Grand Central Dispatch(GCD)执行一些进程。这些进程正在以我认为是线程安全的方式操作一些核心数据NSManagedObject(非基于文档的)(创建一个新的ManagedObject上下文以在该线程中使用)

我遇到的问题是,当调度队列仍在运行时,用户试图退出应用程序

在实际退出之前正在调用NSApplication委托

- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender 
我收到一个错误“无法合并更改”。这在某种程度上是意料之中的,因为仍有操作通过不同的managedObjectContext执行。然后,我将看到由核心数据应用程序生成的模板中的NSAlert

在中有一个名为“在退出时注意线程行为”的部分,其中暗指使用方法。我在实现这一点上有点困难

我希望我的应用程序完成对排队项目的处理,然后在不向用户显示错误消息的情况下终止。更新视图或使用工作表让用户知道应用程序正在执行某些操作,并在操作完成时终止,这也会很有帮助

我将在何处以及如何实施此行为

解决方案: 所以我这里有一些不同的问题

  • 我在
    调度队列中有访问核心数据的块
    阻止我的应用程序正常终止

  • 当我尝试向调度队列添加新项目时,调度队列的一个新实例在一个新线程上启动

  • 我解决这个问题的方法是在我的
    AppDelegate
    中使用
    NSNotificationCenter
    (其中
    (NapplicationTerminateReply)应用程序应终止:(Napplication*)发送方
    。在核心数据生成的模板代码中添加以下内容:

    // 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
    ):

    我还创建了一个
    struct
    ,可以检查用户是否想要终止应用程序。(我在上面的
    awakeFromNib
    中设置了struct的初始状态)。将
    struct
    放在
    @synthesis
    语句之后:

    struct {
        bool isAppTerminating;
        bool isTerminatingNow;
    } appTerminating;
    
    现在,对于长时间运行的
    dispatch\u queue
    ,它阻止应用程序正常终止。当我最初创建此
    dispatch\u queue
    时,使用for循环添加需要更新的项。在执行此for循环后,我添加了另一个队列项,该队列项将检查
    结构
    ,以查看电子应用程序应终止:

    // 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);
        }
    }
    

    我自己没有处理过这个问题,但从我阅读的文档来看,您应该做的是:

  • 应用程序应终止:
    返回
    NSTerminateLater
    。这会让系统知道您的应用程序尚未准备好终止,但很快就会终止
  • 将调度队列上的“最终”块排队。(您需要确保其他块在此之后没有排队。此块将在执行所有其他工作后运行。请注意,队列必须是串行的,而不是并发队列中的一个),才能正常工作。)“最终”块应该执行
    [NSApp replyToApplicationShouldTerminate:是];
    ,这将完成正常的终止过程

  • 没有任何直接的方法可以确定GCD队列是否仍在工作。要处理此问题,您唯一可以做的另一件事(据我所知)是将所有块放入一个队列中,然后在
    应用程序中等待组houldTerminate:
    (使用.

    只是为了澄清:您是否正在从
    应用程序返回
    nsterminater
    ,如
    回复应用程序应终止:
    文档中所述?不,我不知道。我不知道该添加到哪里。那么,您返回的是什么?现在它只是核心数据生成的默认样板文件。所以如果用户选择“无论如何退出”,它会弹出一个警报,提示“退出时无法保存更改。无论如何退出?”NSTerminateNow返回,否则NSTerminateCancel返回。如果您不能立即终止,您应该设置您需要的任何内部状态,以向应用程序的其他部分发出信号,表明它们需要关闭并返回
    NSTerminateLater
    。在模板代码中,您可以在
    -applicationshouldtimate:
    或仅在
    if(!\uu managedObjectContext)之后执行此操作…
    部分。然后,当关闭过程完成后,在主线程上调用
    -replyToApplicationShouldTerminate:
    。似乎已经到了一半。出于某种原因,调度队列是并发运行的,而不是串行运行的。我已尝试将队列创建为
    跟踪队列=调度队列\u创建(“com.example.addtracking”,NULL);
    trackingQueue=dispatch\u queue\u create(“com.example.addtracking”,dispatch\u queue\u SERIAL”);
    我还在创建一个调度组:
    refreshGroup=dispatch\u group\u create();
    正在将项目添加到调度组:
    dispatch\u group\u async(refreshGroup,trackingQueue^{NSLog(@“some action goes here”);});
    无论队列是串行队列还是并发队列,等待组都应该有效(串行队列也是默认队列)。是什么让你认为它是并发的?你确定在点击
    应用程序shouldtimiter:
    后,没有其他任何东西正在排队吗?我将一些调试内容记录到每个块中的控制台,以便查看发生了什么。当我将terminate块添加到队列中时,该块在其他项之前执行
    - (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);
        }
    }