I have my main application delegate which contains a method that returns an object. This application delegate runs on the main thread.
I also have a NSOperation that get
If you simply need to protect a critical section of code, why not using the Objective-C @synchronized directive? Of course, using NSLock will also work, but you need to explicitly manage the NSLock instance. From the documentation:
Objective-C supports multithreading in applications. This means that two threads can try to modify the same object at the same time, a situation that can cause serious problems in a program. To protect sections of code from being executed by more than one thread at a time, Objective-C provides the @synchronized() directive.
The @synchronized()directive locks a section of code for use by a single thread. Other threads are blocked until the thread exits the protected code; that is, when execution continues past the last statement in the @synchronized() block.
The @synchronized() directive takes as its only argument any Objective-C object, including self. This object is known as a mutual exclusion semaphore or mutex. It allows a thread to lock a section of code to prevent its use by other threads. You should use separate semaphores to protect different critical sections of a program. It’s safest to create all the mutual exclusion objects before the application becomes multithreaded to avoid race conditions.
Listing 12-1 shows an example of code that uses self as the mutex to synchronize access to the instance methods of the current object. You can take a similar approach to synchronize the class methods of the associated class, using the Class object instead of self. In the latter case, of course, only one thread at a time is allowed to execute a class method because there is only one class object that is shared by all callers.
Listing 12-1 Locking a method using self
- (void)criticalMethod
{
@synchronized(self) {
// Critical code.
...
}
}
Do you absolutely need to perform this invocation on the NSOperation
thread, and not simply provide the required object as part of creating the custom operation?
If so, I would recommend not using locks unless performance is critical. If the iPhone supported it, you could use Grand Central Dispatch to get the object onto your thread:
__block id newObject = nil;
dispatch_sync(dispatch_get_main_queue(), ^{
newObject = [[[[UIApplication sharedApplication] delegate] myMethod] retain];
});
For the iPhone, I would be tempted to create a helper method:
- (void)createNewObject:(NSValue *)returnPtr {
id newObject = [[[[UIApplication sharedApplication] delegate] myMethod] retain];
*(id *)[returnPtr pointerValue] = newObject;
}
And invoke it like so from your NSOperation
thread:
id newObject = nil;
[self performSelectorOnMainThread:@selector(createNewObject:)
withObject:[NSValue valueWithPointer:&newObject]
waitUntilDone:YES];
Actually performing the execution on the main thread has fewer implicit risks.
Unless you explicitly write code to cause something to execute on another thread, every method call is going to be executed directly on the thread it was called upon. There is no magic with a method call. You can think of it as having the exact same semantics/ABI as a C function call for the purposes of threading.
Your locking pattern will work fine to ensure exclusive access across threads.
Two additional unrelated notes (because so many people trip over it):
declaring a property as atomic
has little to do with thread safety. Atomicity only guarantees that you get a valid value, not the correct value (there is a difference).
autoreleased objects are never safe to pass between threads. You need an explicit retain
on the sending thread and a balancing eventual release
on the receiving thread.