12

我对使用NSOperation和绘图有一些建议:

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

NSOperation做了一些繁重的处理,它打算在它的 main() 方法中循环几分钟,不断处理一些工作,但现在我只有一个 while() 循环,里面有一个 sleep(1),它被设置为 go大约只有 5 次(用于测试)。

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

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

通知立即进入主线程,看起来是异步的,看起来还不错。似乎两个线程都按预期运行......也就是说 - 同时运行。(我使用 NSLog() 只是为了大致检查每个线程何时发送和接收通知)。

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

但是:这是我的问题(抱歉,这里花了一点时间):当主线程(视图)收到来自 NSOperation 的通知时,它会更新这个测试整数并调用 [self setNeedsDisplay]。但是,在 NSOperation 完成之前,视图不会重绘自身!我预计 NSOperation 作为一个单独的线程将无法阻止主线程的事件循环,但似乎这就是正在发生的事情。当 NSOperation 完成并且它的 main() 返回时,视图最终会立即重新绘制自己。

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

非常感谢任何帮助或建议,如果您想查看一些代码,请告诉我。

4

2 回答 2

10

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

因此,在该方法中,您可以使用performSelectorOnMainThread:withObject:waitUntilDone:.

例如:

我的操作.m

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

我的视图控制器.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;
}
于 2010-01-21T20:26:53.447 回答
0

大多数人都知道,任何与显示相关的任务都必须在主线程上完成。然而,作为直接结果,任何可能影响绘图的 Cocoa 绑定属性的更改也必须在主线程上进行修改(因为 KVO 触发器将在触发它们的线程上处理)。这让大多数人感到惊讶:尤其是从主线程以外的线程调用 [self setNeedsDisplay] 是不安全的。

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

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

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

于 2015-09-23T02:06:50.587 回答