Objective-C: Releasing an object that spawns a thread
While I was working on the first version of I ♥ Concerts, I ran into an interesting problem trying to release an object that spawns a thread. I have a ConcertUpdater object that handles querying our server for upcoming concerts in your area. This object is initialized in our AppDelegate. Because finding the user’s location and downloading the concert data can take a bit of time, though, the object kicks off new threads for the expensive operations, so the UI remains responsive.
I used an NSOperationQueue to handle the threads like so:
if(!isUpdatingConcerts){ isUpdatingConcerts = (BOOL *)YES; NSInvocationOperation *theOp = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(getConcerts) object:nil]; [appDelegate.concertoQueue addOperation:theOp]; [theOp release]; } |
This threaded approach, however, leads to not knowing exactly when we can release the ConcertUpdater object from memory.
I tried a few different things: simply not releasing it (used version 1.0) and autorelease. Autorelease wouldn’t work, because it would get released before it found a location and downloaded the data. Simply not releasing it works, but is kind of irritating to me—always having to ignore that error in Clang. I also realize this is one of my first projects programming threads, so there might simply be a better way to architect this in general—maybe start the ConcertUpdater’s getConcerts method in a thread.
Anyway, I finally did come up with a way to release the ConcertUpdater. On the last line of it’s updateConcerts method (or at least the last line of that process), I tell the AppDelegate to release the object.
[(id)[[UIApplication sharedApplication] delegate] performSelectorOnMainThread:@selector(releaseConcertUpdater) withObject:nil waitUntilDone:NO]; |
Now, if that method simply released the object—which is what I tried first— I would get a EXC_BAD_ACCESS exception, especially when running it on the device. I’m pretty sure, but not positive, that this was happening because the NSOperationQueue wasn’t quite finshed, at least with it’s garbage collection and it would then try to release or access something in the ConcertUpdater object.
The solution was to pause the main thread at that point for a few seconds until the NSOperationQueue had finished all of its tasks.
- (void)releaseConcertUpdater{ [concertoQueue waitUntilAllOperationsAreFinished]; [concertUpdater release]; } |
That little waitUntilAllOperationsAreFinished method was a lifesaver. Now I can free that memory up for other cool operations.