2010-01-21 58 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中定義。