Im posting this question because I have seen a lot of confusion over this topic and I spent several hours debugging NSOperation subclasses as a result.
The problem is that NSOperation doesnt do you much good when you execute Asynchronous methods which are not actually complete until the asynchronous callback completes.
If the NSOperation itself is the callback delegate it may not even be sufficient to properly complete the operation due to the callback occurring on a different thread.
Lets say you are in the main thread and you create an NSOperation and add it to an NSOperationQueue the code inside the NSOperation fires an Asynchronous call which calls back to some method on the AppDelegate or a view controller.
You cant block the main thread or the UI will lock up, so you have two options.
1) Create an NSOperation and add it to the NSOperationQueue with the following signature:
[NSOperationQueue addOperations:@[myOp] waitUntilFinished:?]
Good luck with that. Asynchronous operations usually require a runloop so it wont work unless you subclass NSOperation or use a block, but even a block wont work if you have to "complete" the NSOperation by telling it when the callback has finished.
So...you subclass NSOperation with something akin to the following so the callback can tell the operation when its finished:
//you create an NSOperation subclass it includes a main method that
//keeps the runloop going as follows
//your NSOperation subclass has a BOOL field called "complete"
-(void) main
{
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
//I do some stuff which has async callbacks to the appDelegate or any other class (very common)
while (!complete && [runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
}
//I also have a setter that the callback method can call on this operation to
//tell the operation that its done,
//so it completes, ends the runLoop and ends the operation
-(void) setComplete {
complete = true;
}
//I override isFinished so that observers can see when Im done
// - since my "complete" field is local to my instance
-(BOOL) isFinished
{
return complete;
}
OK - This absolutely does not work - we got that out of the way!
2) The second problem with this method is that lets say the above actually worked (which it does not) in cases where runLoops have to terminate properly, (or actually terminate at all from an external method call in a callback)
Lets assume for a second Im in the main thread when I call this, unless I want the UI to lock up for a bit, and not paint anything, I cant say "waitUntilFinished:YES" on the NSOperationQueue addOperation method...
So how do I accomplish the same behavior as waitUntilFinished:YES without locking up the main thread?
Since there are so many questions regarding runLoops, NSOperationQueues and Asynch behavior in Cocoa, I will post my solution as an answer to this question.
Note that Im only answering my own question because I checked meta.stackoverflow and they said this is acceptable and encouraged, I hope the answer that follows helps people to understand why their runloops are locking up in NSOperations and how they can properly complete NSOperations from external callbacks. (Callbacks on other threads)