2010-01-21 55 views
12

我以后就用NSOperation和绘制一些建议:NSOperation阻止UI绘画?

我有一个主线程创建我NSOperation子类,然后把它添加到一个NSOperationQueue

NSOperation做一些繁重的处理,它的目的是在main()方法循环几分钟,不停地处理一些工作,但现在我只是有一个while()循环与睡眠(1)内,这是设置为五次左右(用于测试)。

主要(原)螺纹,其派生此NSOperation负责绘制到一个视图和更新UI。

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

的通知通过立即主线程和异步似乎,看着就好了。看来,两个线程按预期运行......这是 - 并发。 (我使用NSLog()来粗略地检查每个线程何时发送和接收通知)。

当视图收到通知时,和其处理程序被调用时,我简单地递增的整数变量,并尝试并绘制这个到视图(当然是一个字符串)。在测试中,drawRect中的代码:将该整数(作为字符串)绘制到屏幕上。

但是:这是我的问题(对不起,它需要一点时间来到这里):当主线程(视图)从NSOperation收到通知时,它会更新此测试整数并调用[self setNeedsDisplay]。但是,直到NSOperation完成之后,视图才会重新绘制!我预计的NSOperation,作为一个单独的线程,就没有能力阻止主线程的事件循环,但现在看来,这是发生了什么。当NSOperation完成并且main()返回时,视图最终会立即重绘。

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

任何帮助或建议大加赞赏,如果你想看到一些代码,只是让我知道。

回答

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; 
} 
+3

如果你愿意,你可以直接从你的NSOperation主要方法中执行(performSelectorOnMainThread:withObject:waitUntilDone :) - 没有任何通知的东西。 – grzaks 2010-09-24 17:48:16

0

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

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

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

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