views:

492

answers:

1

Hi,

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.

Many thanks,

+5  A: 

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;
}
gerry3
You can do that (performSelectorOnMainThread:withObject:waitUntilDone:) straight from your NSOperation main method if you want - without all that notification stuff.
Gaks