NSOperation blocks UI painting?

 ̄綄美尐妖づ 提交于 2019-12-31 13:13:41

问题


I'm after some advice on the use of NSOperation and drawing:

I have a main thread create my NSOperation subclass, which then adds it to an NSOperationQueue.

My NSOperation does some heavy processing, it is intended to loop in its main() method for several minutes, constantly processing some work, but for now I just have a while() loop with a sleep(1) inside, which is set to go around just 5 times (for testing).

The main (original) thread which spawns this NSOperation is responsible for drawing to a view and updating the UI.

I intended to have the NSOperation thread use a notification to tell the main thread that it had done some amount of processing, at the moment this notification is sent once each time it goes through its while() loop (that is; once a second because it's just doing sleep(1)). The main thread (the view), registers to receive these notifications.

The notifications get through to the main thread immediately, and seeming asynchronously, looking just fine. It appears that both threads run as expected... that is - concurrently. (I use NSLog() just to check roughly when each thread sends and receives the notification).

When the view receives a notification, and its handler method is called, I simply increment an integer variable, and try and draw this to the view (as a string of course). In testing, the code in drawRect: draws this integer (as a string) to the screen just fine.

However: here's my problem (sorry it's taken a little while to get here): when the main thread (view) receives a notification from the NSOperation, it updates this test integer and calls [self setNeedsDisplay]. However, the view does not redraw itself until the NSOperation is finished! I expected that the NSOperation, being a separate thread, would have no ability to block the main thread's event loop, but it appears that this is what is happening. When the NSOperation finishes and its main() returns, the view finally redraws itself straight away.

Perhaps I am not using NSOperation correctly. I am using it in "non- concurrent" mode, but despite the name my understanding is that this still produces a new thread and allows for asynchronous processing.

Any help or advice much appreciated, if you'd like to see some code just let me know.


回答1:


The method in the observer that is performed in response to your notification is not being performed on the main thread.

So, in that method, you can force another method to run on the main thread using performSelectorOnMainThread:withObject:waitUntilDone:.

For example:

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



回答2:


Most people know that any display-related task must be done on the main thread. However, as a direct consequence of that, any change to a Cocoa bindings property that may affect the drawing must also be modified on the main thread (because the KVO triggers will be handled on the thread from which they are triggered). This is a surprise to most people: in particular, it is not safe to call [self setNeedsDisplay] from a thread other than the main thread.

As mentioned by gerry, your NSNotification is not processed on the main thread, it is processed on the thread from which it is sent. Hence in your NSNotification handler, you must send your commands back to the main thread if they are display-related or if they affect Cocoa bindings. @performSelector works, but with queues, I have found an easier and more readable method:

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

It avoids the definition of a secondary helper function whose only purpose is to be called from the main thread. mainQueue was defined only in >10.6.



来源:https://stackoverflow.com/questions/2111208/nsoperation-blocks-ui-painting

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!