24
Sep
10

The Great Dealloc Debate

Must have been a slow week for everyone else on the Cocoa programming front; the tempest in a teapot du jour is, wait for it … the proper etiquette for dealing with your releasable instance variables in -dealloc. Yes, really. Everybody agrees that using mutators is a bad idea, because it triggers KVO observers at a remarkably inopportune time would be the most obviously compelling reason. After that … opinions vary. Wildly.

A good point to pick up the debate is Jeff Lamarche’s Dealloc which lines up the options of setting to nil or not, and then Daniel Jalkut goes off in Don’t Coddle Your Code about how apps should crash if something’s wrong not hide it, and then Messr. Lamarche follows up with More on dealloc providing another pattern for super-defensive multithreading, and blahblahblah. One of the more interesting blahblahblah’s is digdog’s To nil, or not to nil, that is the question where he builds an interesting case for niling which turns out to be based on a compiler bug about not allowing access to synthesized ivars. Which you should realize is a bug not a design, if you don’t like we didn’t.

Personally, we come completely down on the side that software you put in the App Store should NEVER crash if POSSIBLY avoidable, because you can’t distribute a fix without a submission cycle! And niling out will avoid almost all crashes. So you should do that. If as in Messr. Jalkut’s ludicrously contrived example the effect of [receiver method] being called when receiver is nil is the wrong thing, well then test that receiver exists before calling it. We pretty much tend to follow a pattern that does that anyway whenever there’s a momentous decision at stake through leftover C++ habits, so that doesn’t strike us as ineffably wrongheaded as some more Cocoaheaded purist might find it.

However, we also come completely down on the side of finding whatever bugs you can before release. That’s not actually an argument for leaving the pointers dangling as most recommend; we’ve seen too many completely wacky behaviours in completely unrelated object YYY being caused by a dangling pointer in deallocated object XXX wasting more time to track down than the behaviour of a niled instance would have been noticed to think that seriously is all that good an idea.

No, what you really want is something like NSZombieEnabled without the overhead for your development cycles, and as non-crashing as possible in something the user gets through the App Store. So we vote for something along what Kevin Ballard provided as the very first comment to the “Dealloc” post [UPDATE: Now edited to compile correctly]:

#if DEBUG
#define MCRelease(x) do { [x release]; if (!getenv("NSZombieEnabled")) x = (id)0xDEADBEEF; } while (0)
#else
#define MCRelease(x) [x release], x = nil
#endif

Yes. Yes, that would be the One True And Correct Way™ to handle releasing your variables in -dealloc, wethinks; and the entire rest of the ongoing debate is just a waste of time. But if there’s some actual problem with that strategy, be sure to throw your two cents worth in. At the rate this thing is going, we’ll be able to retire the national debt in no time…


7 Responses to “The Great Dealloc Debate”


  1. 1 Peter Sep 26th, 2010 at 3:05 am

    This does not work per se.

    if you change the line to

    #define MCRelease(x) do { [x release]; if (!getenv(“NSZombieEnabled”)) x = 0xDEADBEEF; } while (0)

    it works somewhat. But you get the error

    “Assignment makes pointer from integer without a cast”.

    If you try casting the x, you get
    “target of assignment not really an lvalue; this will be a hard error in the future”

    any thoughts?

  2. 2 Alex Sep 26th, 2010 at 7:29 am

    > If you try casting the x, you get “…this will be a hard error…”

    Correctly so, yes. What you should do is assign x = (id)0xDEADBEEF; which will be compatible with any type calling release on is a correct thing, no doubt. Will edit post to clarify, thanks!

  3. 3 Bill Sep 26th, 2010 at 7:34 am

    Thank you. May you be touched by His Noodly Appendage. :-)

  4. 4 Bill Sep 26th, 2010 at 9:47 am

    Enabling NSZombieEnabled runtime behavior and setting a released object’s pointer value to 0xDEADBEEF is redundant.

    From NSDebug.h,

    FOUNDATION_EXPORT BOOL NSZombieEnabled;
    // Enable object zombies. When an object is deallocated, its isa
    // pointer is modified to be that of a “zombie” class (whether or
    // not the storage is then freed can be controlled by the
    // NSDeallocateZombies variable). Messages sent to the zombie
    // object cause logged messages and can be broken on in a debugger.
    // The default is NO.

    When NSZombieEnabled is YES, the object that was messaged after deallocation is readily identified by a log message and in gdb. Assigning the deallocated object’s pointer value to 0xDEADBEEF adds just a tiny bit of extra information to the situation.

    You would obtain the debug benefit of using the 0xDEADBEEF sentinel value without the overhead of running with NSZombieEnabled = YES by just being conditional on DEBUG.


    #if DEBUG
    #define MCRelease(x) [x release]; x = (id)0xDEADBEEF
    #else
    #define MCRelease(x) [x release], x = nil
    #endif

    Just to muddy the water further, you could alternately use a static inline function instead of the C macro.


    static inline void Release (id target) {
    [target release];
    #if DEBUG
    target = (id))0xDEADBEEF;
    #else
    target = nil;
    #endif
    }

  5. 5 Alex Sep 26th, 2010 at 9:57 am

    @Bill:

    You’re missing the point that our macro is designed to *not* interfere with normal NSZombieEnabled debugging, by leaving the pointer dangling when that environment variable is set.

    That way we’ll get the expected behaviour when NSZombieEnabled is set, but as most commonly we do want to have our objects actually released as NSZombieEnabled stops happening, when it’s not running we set the pointer to 0xDEADBEEF which under almost all circumstances ought to show us any problems we have with a hard crash pretty much as quickly as running with NSZombieEnabled would.

  6. 6 Bill Sep 26th, 2010 at 10:37 am

    Well, no, you can’t use a static inline function, at least as written. Just stick with macros.

  1. 1 Tweets that mention The Great Dealloc Debate at Under The Bridge -- Topsy.com Pingback on Sep 25th, 2010 at 1:41 pm

Leave a Reply