Next article: How To Shrink Your Source Code
Previous article: Why CoreAudio is Hard
Tags: autorelease bug cocoa objectivec
I just hit a subtle but commonly known bug for the first time. I thought I'd share my fun with the world. Everybody reading this blog should know about autorelease pools and how they work in Cocoa. As everybody knows, every time you go through the event loop, Cocoa blows away the old pool and makes a new one for you, so that all of your autoreleased objects go away and your new ones go into a fresh pool. That way you never build up more objects than get produced during a single event loop cycle.
The key word is "event loop". In Apple's infinite wisdom, things that aren't real actual NSEvents don't trigger the pool.
I'm currently working on an app that spends a lot of time in the background doing dark, unspeakable things with NSStreams on the main thread. I encountered a bug where one of my objects can get destroyed in the middle of handling a stream event, which left it open to getting other stream events after it was deallocated. (Apparently NSStreams can still send you stream events even after you've closed them, released them, and set their delegate to
nil, but that's an entirely different problem for another day.)
The obvious fix was to simply do
[[self retain] autorelease]before making the problem call. And fix it it did, except instead of my
deallochappening in the middle of my event handler, it never happened at all.
Until I clicked on my app's dock icon.
At least the solution was easy. Post an
NSApplicationDefinedevent in the stream event handler, and autoreleased objects get destroyed on schedule.
The amazing part is that, from what I've heard, this bug has been there for a long time. I haven't used it, but it seems like CFRunLoopObserver would make it trivial to hook into the runloop at such a low level that you could drain the current pool if anything so much as blinks. And as we all know, Autorelease is Fast, so there should be no penalty in doing this.
How many of our apps sit quietly ticking away in the background, accumulating ever larger autorelease pools that only get drained once we bring them forward? It makes you wonder.
Hopefully this will be fixed in Leopard. I'm afraid to file a bug lest I get a "Behaves Correctly".
Comments RSS feed for this page
Add your thoughts, post a comment:
Spam and off-topic posts will be deleted without notice. Culprits may be publicly humiliated at my sole discretion.