NSOperation阻止UI绘画?

时间:2010-01-21 17:09:50

标签: ios multithreading cocoa-touch nsoperation nsoperationqueue

我正在使用NSOperation和绘图:

我有一个主线程创建我的NSOperation子类,然后将其添加到NSOperationQueue

我的NSOperation执行了一些繁重的处理,它打算在main()方法中循环几分钟,不断处理一些工作,但是现在我只有一个带睡眠的while()循环(1 )内部,设置为5次左右(用于测试)。

产生此NSOperation的主(原始)线程负责绘制视图并更新UI。

我打算让NSOperation线程使用通知来告诉主线程它已经完成了一些处理,此时每次通过while()循环时发送一次通知(即;一次)一秒钟,因为它只是在做睡眠(1))。主线程(视图)注册接收这些通知。

通知立即进入主线程,看起来是异步的,看起来很好。似乎两个线程都按预期运行......即 - 同时运行。 (我使用NSLog()来粗略检查每个线程何时发送和接收通知)。

当视图收到通知并调用其处理程序方法时,我只是递增一个整数变量,并尝试将其绘制到视图中(当然是一个字符串)。在测试中,drawRect中的代码:将此整数(作为字符串)绘制到屏幕上就好了。

然而:这是我的问题(对不起,这需要花一点时间才能到达):当主线程(视图)收到来自NSOperation的通知时,它会更新此测试整数并调用[self setNeedsDisplay]。但是,在NSOperation完成之前,视图不会重绘!我期望NSOperation作为一个单独的线程,无法阻止主线程的事件循环,但看起来这就是正在发生的事情。当NSOperation完成并且其main()返回时,视图最终会立即重新绘制。

也许我没有正确使用NSOperation。我在“非并发”模式下使用它,但尽管我的理解是这仍然产生一个新线程并允许异步处理。

如果您希望看到某些代码,请与我们联系。

2 个答案:

答案 0 :(得分:10)

在主线程上没有执行响应您的通知而执行的观察者中的方法。

因此,在该方法中,您可以使用performSelectorOnMainThread:withObject:waitUntilDone:强制另一个方法在主线程上运行。

例如:

<强> MyOperation.m

- (void)main {
    for (int i = 1; i <= 5; i++) {
        sleep(1);
        [[NSNotificationCenter defaultCenter] postNotificationName:@"GTCNotification" object:[NSNumber numberWithInteger:i]];
    }
}

<强> MyViewController.m

- (void)setupOperation {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(myNotificationResponse:) name:@"GTCNotification" object:nil];

    NSOperationQueue *opQueue = [[NSOperationQueue alloc] init];
    MyOperation *myOp = [[MyOperation alloc] init];

    [opQueue addOperation:myOp];

    [myOp release];
    [opQueue release];
}

- (void)myNotificationResponse:(NSNotification*)note {
    NSNumber *count = [note object];
    [self performSelectorOnMainThread:@selector(updateView:) withObject:count waitUntilDone:YES];
}

- (void)updateView:(NSNumber*)count {
    countLabel.text = count.stringValue;
}

答案 1 :(得分:0)

大多数人都知道任何与显示相关的任务都必须在主线程上完成。但是,作为其直接结果,还必须在主线程上修改可能影响绘图的Cocoa绑定属性的任何更改(因为KVO触发器将在触发它们的线程上处理)。这对大多数人来说是一个惊喜:特别是,从主线程以外的线程调用[self setNeedsDisplay]是不安全的。

如gerry所述,您的NSNotification不会在主线程上处理,它会在发送它的线程上处理。因此,在NSNotification处理程序中,如果它们与显示相关或者它们影响Cocoa绑定,则必须将命令发送回主线程。 @performSelector工作,但是有了队列,我发现了一种更简单,更易读的方法:

[ [ NSOperationQueue mainQueue] addOperationWithBlock:^(void) {
    /* Your main-thread code here */ }];

它避免了辅助辅助函数的定义,其唯一目的是从主线程调用。 mainQueue仅在&gt; 10.6中定义。