Dealloc method in iOS and setting objects to nil
Asked Answered
V

4

10

I have a pretty basic question. In some examples I've seen, objects are just released in the dealloc method. In others, the objects are released and then set to nil. Is there a reason for this? Is setting to nil after releasing advantageous?

Volition answered 21/7, 2011 at 15:39 Comment(0)
H
23

Three ways to dealloc

1. Just release

- (void)dealloc {
    [airplane release];
    [super dealloc];
}

Now the object reference points to a random position, which may be one of two things:

  1. Most likely it is garbage, because the memory position can't be interpreted as an object.
  2. Rarely it will be a different object, because memory have been reused to create a new object.

The effect of a further method calls through this pointer is one of these three (which one is undefined):

  • A crash with EXC_BAD_ACCESS because the pointer points to garbage.
  • A crash with undefined selector because it points to a valid object which doesn't have that method.
  • A successful method execution because the new object has a method by the same name.

2. Release and nil

- (void)dealloc {
    [airplane release], airplane = nil;
    [super dealloc];
}

Now the object reference is nil and any further method calls are ignored. This may silently cause a defined but unforeseen lateral effect in your code, but at least it doesn't crash your application.

3. Nil and release

- (void)dealloc {
    id temp = airplane;
    airplane = nil;
    [temp release];
    [super dealloc];
}

This is the same as before, but it removes that small window between release and nil where the object reference points to an invalid object.

Which one is best?

It is a matter of choice:

  • If you rather crash choose just release.
  • If you rather ignore the mistake choose nil+release or release+nil.
  • If you are using NSZombieEnabled=TRUE then just release, don't nil the zombie!

Macros and zombies

A easy way to defer your choice is using a macro. Instead [airplane release] you write safeRelease(x) where safeRelease is the following macro that you add to your .pch target file:

#ifdef DEBUG
  #define safeRelease(x) [x release]
#else
  #define safeRelease(x) [x release], x=nil
#endif

This macro doesn't respect zombies. Here is the problem: when NSZombieEnabled is TRUE the object turns into a NSZombie. If you nil its object reference, any call sent to him will be ignored.

To fix that, here is a macro from Kevin Ballard that sets the pointer to an invalid made up reference ONLY when NSZombieEnabled is FALSE. This guarantees a crash during debug time if zombies are not enabled, but leaves the zombies be otherwise.

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

References

Apple doesn't have a recommendation on which one is best. If you want to read the thoughts of the community here are some links (the comment threads are great too):

Hysterectomize answered 21/7, 2011 at 19:40 Comment(6)
This was a great answer. Thank you!Volition
"in which case your app crashes" in WHAT case your app crashes? "Your call crashes" WHAT call? I don't see anyone asking about any callFiord
I was referring (in my head) to method calls on the released pointer. I edited for clarification.Hysterectomize
Recommended you use arc in ios7. you can't call deallocToffic
In an implementation of dealloc, do not invoke the superclass’s implementation. developer.apple.com/reference/objectivec/nsobject/…Leman
@Leman this post was written before ARC. In ARC the call to super is automatic, it is a compiler error to call super dealloc manually.Hysterectomize
A
3

This snippet covers all the bases and is ready to cut and paste into a .pch file.

// SAFE_RELEASE
//      Releases an object, then does other things based on context.
//
//      The intention is to fail early during internal testing but prevent
//          customers from experiencing crashes if at all possible.
//
// For more information see:
//      https://mcmap.net/q/1046774/-dealloc-method-in-ios-and-setting-objects-to-nil
//
// Debug build:
//      If zombies are enabled, the macro just calls |release|. The zombie
//          mechanism will continue to be used to find messages sent to
//          the deallocated object.
//      Otherwise, zombies are not enabled, so the macro sets the object to a
//          invalid memory address. (0xDEADBEEF.) This will intentionally
//          cause a crash if the object is used, allowing the bug to be found
//          and fixed immediately.
//
// Release build:
//      The macro calls |release| normally. Then it sets the object to nil to
//          prevent a possible crash caused by sending a message to a
//          deallocated object. Messages sent to nil are always allowed.
//
#if DEBUG
#define SAFE_RELEASE(x) \
    do { \
        [x release]; \
        if (!getenv("NSZombieEnabled")) \
            x = (id)0xDEADBEEF; \
    } while (0)
#else
#define SAFE_RELEASE(x) \
    [x release], x = nil
#endif

The code is functionally equivalent to Jano's second version of safeRelease, but adds documentation and compliance with Google's coding standards.

Aerie answered 28/6, 2013 at 21:15 Comment(0)
C
1

If there's a dealloc call in more than one place, setting the object variable to nil makes sure it won't be deallocated more than once by mistake. Same logic if the function with the dealloc is called from more than one place, or can be called arbitrarily by external code (i. e. other classes).

In real life, this typically happens when the enclosing object is "reusable" - supports multiple rounds of content initialization/teardown. The nil-ness of object pointers then becomes a valuable component of object state - it means that the object is "empty" right now.

Sorry for the generalities.

Charlot answered 21/7, 2011 at 15:46 Comment(3)
"If there's a dealloc call in more than one place" What does "dealloc call in more than one place" mean? You are not supposed to call deallocFiord
When this answer was written, you were supposed to :) Automatic Reference Counting (ARC) was not always around.Charlot
No, you were NEVER supposed to call dealloc yourself, except for [super dealloc]Fiord
A
0
- (void)dealloc
{
     [searchPlace release];
     [super dealloc];
}
- (void)viewDidUnload
{
     [super viewDidUnload];
     self.searchPlace = nil;
}

Is this like what you say?

Antislavery answered 21/7, 2011 at 15:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.