I’ve come across an issue with using a third party library, and am not sure what the common pattern to solve it is.
I’m using the asi-http-request class, which fetches http objects asynchronously using a thread.
In my objects dealloc() method, I do
[request setDelegate:nil];
[request release];
However the delegate is sometimes still called after this has happened. (I can see when this happens the delegate field of the request object is nil.) This sometimes causes a crash if the delegate has been destroyed already.
I believe this is a race condition. The code from ASIHTTPRequest that calls the delegate looks like this:
// Let the delegate know we are done
if ([self didFinishSelector] && [[self delegate] respondsToSelector:[self didFinishSelector]]) {
[[self delegate] performSelectorOnMainThread:[self didFinishSelector] withObject:self waitUntilDone:[NSThread isMainThread]];
}
The problem happens if the performerSelectorOnMainThread has been called (but not completed) when the setDelegate call happens on the main thread.
One solution would be to add a wrapper around ‘didFinishSelector’ that checks (on the main thread) that the delegate is still non-nil before calling the selector, but this would result in a lot of wrappers.
There is some background here:
http://groups.google.com/group/asihttprequest/browse_thread/thread/721220b9645f4a42
All suggestions on the “normal” solution for this appreciated!
Thanks
Joseph
My original thoughts (wrapper around ‘didFinishSelector’ that checks on the main thread that the delegate is still non-nil before calling the selector) turned out to be the correct solution, as confirmed by helpful folks over on the apple dev forums:
https://devforums.apple.com/message/255935#255935
To avoid my worry of ending up with lots of wrappers, I managed to create only a single wrapper:
then when I want to call the delegate, the code just looks something like this:
As best I can tell from experiments & code analysis (and assuming setDelegate is only called from the main thread), this is 100% safe. It could be made safe for the non-main thread calls to setDelegate by taking the object lock inside callSelectorCallback.