表格和长期运行的任务

时间:2011-01-10 09:54:17

标签: cocoa grand-central-dispatch nspanel

我需要在用户点击按钮后运行复杂(即长)任务。 该按钮打开工作表,使用dispatch_async和其他Grand Central Dispatch工具启动长时间运行操作。

我已经编写了代码并且工作正常但我需要帮助才能理解我是否已经正确地完成了所有操作,或者我是否因为无知而忽略了任何潜在的问题。

用户单击按钮并打开工作表,该块包含长任务(在此示例中,它仅运行for(;;)循环 该块还包含在任务完成时关闭工作表的逻辑。

-(IBAction)openPanel:(id)sender {
    [NSApp beginSheet:panel
       modalForWindow:[self window]
        modalDelegate:nil
       didEndSelector:NULL
          contextInfo:nil];

    void (^progressBlock)(void);
    progressBlock = ^{

        running = YES; // this is a instance variable

        for (int i = 0; running && i < 1000000; i++) {
            [label setStringValue:[NSString stringWithFormat:@"Step %d", i]];
            [label setNeedsDisplay: YES];
        }
        running = NO;
        [NSApp endSheet:panel];
        [panel orderOut:sender];

    };

    //Finally, run the block on a different thread.
    dispatch_queue_t queue = dispatch_get_global_queue(0,0);
    dispatch_async(queue,progressBlock);
}

该面板包含一个停止按钮,允许用户在完成任务之前停止该任务

-(IBAction)closePanel:(id)sender {
    running = NO;
    [NSApp endSheet:panel];
    [panel orderOut:sender];
}

1 个答案:

答案 0 :(得分:2)

此代码在设置状态文本的值时存在潜在问题。基本上,AppKit中的所有对象只允许从主线程调用,如果不是,可以以奇怪的方式打破。您正在从运行全局队列的任何线程调用标签上的setStringValue:setNeedsDisplay:方法。要解决此问题,您应该像这样编写循环:

for (int i = 0; running && i < 1000000; i++) {
    dispatch_async(dispatch_get_main_queue(), ^{
        [label setStringValue:[NSString stringWithFormat:@"Step %d", i]];
        [label setNeedsDisplay: YES];
    });
}

这将在AppKit期望的主线程中设置标签文本。