views:

256

answers:

3

I have an object that when you instantiate it and send it a message it takes a long time to run. It is designed to call back a delegate object with the results of the job. There are multiple calls back to the delegate over time.

I know I need to put the job in a thread. I know how to make a thread. I know that if a thread wants to send a message it needs to call performSelectorOnMainThread. But thats as much as I can work out so far.

Are there any tutorials on how to actually put all this together? At the moment im really just guessing.

+2  A: 

A nice introduction is Apple's Concurrency Programming Guide.

If you need specific information about threads, you can also take a look at Threading Programming Guide.

After these readings, NSOperation, NSThread, Grand Central Dispatch, openCL and how to design concurrent applications on Mac OS X will have no secret to you.

mouviciel
+1  A: 

The easiest way is to use NSOperation combined with a NSOperationQueue.

Have a look at Apple's Concurrency Programming Guide.

Georg
Nice option, however I should have made clear, the work process needs to be able to send back multiple messages to the delegate over time, I dont think the NSOperation queue can do that.
Jacob
It can. Just subclass it and use `performSelectorOnMainThread` (or invoke it directly if it's not about updating the gui)
Georg
or KVO or Notifications…
Georg
+3  A: 

I would suggest using a NSOperationQueue with a NSInvocationOperation. The NSOperationQueue will automatically scale to use as many threads as is optimal under current load, and is intended for executing long running tasks. There is nothing that stops you from calling back to your delegate several times from an operation running on a operation queue.

Setup your queue like this:

NSOperationQueue* queue = [[NSOperationQueue alloc] init];

I assume that you want no extra arguments sent to your task but the delegate to report back to. In this scenario using a plain NSInvocationOperation is easiest. I also assume that the method that implements the task, and the method that receives the callback are in the same class. Start a task like so:

NSInvocationOperation* operation;
operation = [[NSInvocationOperation alloc] initWithTarget:self
                                                 selector:@selector(executeTask:)
                                                   object:self];
[queue addOperation:operation];
[operation release];

And then implement executeTask:. Call back to the delegate on the main thread, so that the UI can safely be updated in the callback. You could wait for the callback to complete if you like, but I skip this, all callbacks will be queued on the main thread.

-(void)executeTask:(id)delegate;
{
  while (notDone) {
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    [delegate performSelectorOnMainThread:@selector(taskWillProgress:)
                               withObject:self
                            waitUntilDone:NO];
    // Do stuff
    [pool release];
  };
}

I have also thrown in a local autorelease pool. This can be necessary when doing lengthy work in background tasks without a run-loop in place. Good practice.

PeyloW
Excellent answer, though I prefer to subclass NSOperation to using the pre-built operations.
Georg
Using the pre-built operations works quite nice when only having a single argument. `NSBlockOperation` on Mac OS X 10.6 works for everything and is super nice. But for iPhone subclassing `NSOperation` is usually the right way to go.
PeyloW