4

I know that in Objective-c there's a very easy way to declare variables like this:

NSArray* myArray;

@property(retain) NSArray* myArray;

@synthesize myArray;

This way you can use self.myArray as both the setter and getter while retaining the variable. However this will also allow you to do one more thing, which is avoiding yourself using dealloc. As far as I understand, this two lines are the same:

self.myArray = nil;
[myArray release];

My question is, which one is the preferred way? Is there any cases where one of them will work and the other one won't?

EDIT: Sorry, I meant release, not dealloc...

Enrico Susatyo
  • 18,061
  • 17
  • 89
  • 153

4 Answers4

4

You should never call dealloc yourself (except under very unorthodox circumstances).

Instead of dealloc, you should call [myArray release] and let the released process take care of this for you.

Have a look here for way more info than you'd probably like on a dealloc method as well.

slycrel
  • 4,145
  • 2
  • 27
  • 29
  • Sorry, I was meaning to write [myArray release]. But is that actually the same as self.myArray = nil? – Enrico Susatyo Dec 31 '10 at 23:10
  • self.myArray = nil does call release in your case. However, keep in mind that self.myArray = nil is the same as [self setMyArray:nil]. So if you override this yourself, make sure you do the right thing. – slycrel Jan 01 '11 at 00:11
  • I tried running my app using Instruments, and there doesn't seem to be any leaks when using self.myArray = nil though. Is this because I've synthesized retain property of that variable? – Enrico Susatyo Jan 02 '11 at 11:13
  • Yes, that's correct -- a synthesized property with a retain on it will retain/release for you with an = operation. – slycrel Jan 03 '11 at 02:48
3

Updates at bottom.

When working with (retain) synthesized properties, the best way to do your dealloc duties is to set the property to nil. The reason I say that this is the "best" way is that it ensures that all the contracts implied by the property declaration are met. For instance, if your property was declared atomic (which it will be, unless you specifically declare it nonatomic) the ONLY way to guarantee that the unsetting of this property on dealloc is done with the same atomic guarantees is to set it to nil using the property in dealloc. It also means that it will behave correctly with regard to any Key-Value Observations of your object - This can be important especially if you use Cocoa Bindings.

When doing your own memory management for a (perhaps private) instance variable without a corresponding property, there are several idioms. The simplest, but most dangerous, is to simply release the iVar, like so:

- (void)dealloc
{
    [myArray release];

    [super dealloc];
}

This will cause the retain on the iVar to be released, but as others mentioned, will leave the now-potentially-stale pointer around to potentially, if erroneously, be accessed by stale or non-retained pointers that may exist pointing to the object being dealloced. Next along the way is the idiom suggested by another answer:

- (void)dealloc
{
    [myArray release], myArray = nil;

    [super dealloc];
}

An even safer, if more pedantic, idiom for this is:

- (void)dealloc
{
    id temp = myArray;
    myArray = nil;
    [temp release];

    [super dealloc];
}

This further limits the chances for a stale read of the pointer by clearing the iVar BEFORE releasing the object pointed to. But, due to the potential for instruction reordering, even this is not a 100% guarantee against a stale read on all architectures.

Although there's much more to say on the topic of concurrency and memory management, in general, if you have a @synthesized property setter, you should just use it in dealloc. Doing so means that if you change the behavior of the @property the dealloc behavior will automatically be correct with respect to the @property declaration.

IMPORTANT NOTE: Using atomic properties != thread safety. (In fact, if you ask me atomic properties are a waste, but...) See here for more details.

UPDATE

This recently got upvoted again, and while I stand by what I said here about the atomic guarantees with synthesized retain properties, and some other content in the original answer is of value on its own, I feel the need to tell the other side of the story. Dave DeLong alluded to some of this in the comments, but I figured it'd be worth adding the detail to the main answer.

I maintain it's true that the only way to maintain the atomicity guarantees is to set the property to nil via the setter. BUT you shouldn't care, and here's why: If an object is being dealloced, that means (if your object graph is correct) that there should be no living references to that object. If there are no living references to the object, then it's not possible for anyone to be caring about the atomicity guarantees of the operation that clears the property.

I also mentioned KVO in the original answer as a reason to use the setter in dealloc, but Dave DeLong mentioned KVO in the comments as a counterpoint. He's right, and here's why: Again, if an object is being dealloced, all KVO observers should already have been removed from it (again, there should be no living references, KVO or not). Indeed, if this is not the case it won't be long until you'd see a console message telling you that your object went away with observations still in place.

In short, while you can't make atomicity guarantees equivalent to those of a synthesized setter in dealloc, it should never matter (and if it does, something else is broken.)

Community
  • 1
  • 1
ipmcc
  • 28,584
  • 4
  • 78
  • 135
  • So you're saying that those precautions are neccesary if we're doing some multi threading programming in the application itself? If I'm not doing any multi threading then just using [myArray release] is ok? – Enrico Susatyo Jan 02 '11 at 11:15
  • These days, you can't be 100% sure that there isn't multi-threading going on behind your back in the Kit (there almost always is). In general, yeah, sure, if you've got a single-threaded app, you shouldn't have to worry about it. But unless you've seen 'dealloc > setFoo:' in your Instruments CPU Sampler/Time Profiler profiles, making that change is 'premature optimization.' And frankly, if you ARE seeing this make a difference, you're doing something else VERY wrong. – ipmcc Jan 02 '11 at 21:04
  • Alright... But did you imply in your answer, that if I have @property (retain) in that variable; I can do self.myVar = nil, it will deallocate that variable and setting it to nil? (same as the second dealloc situation that you gave) – Enrico Susatyo Jan 03 '11 at 00:49
  • Yes, that is correct. With: '@property (retain) NSObject* foo;' Setting 'self.foo = nil;' will definitely cause -release to be called on the previous value and the corresponding iVar will be set to nil. – ipmcc Jan 03 '11 at 13:06
  • 1
    -1 Setting the property to `nil` in `dealloc` is **DISCOURAGED BY THE DOCUMENTATION**. Invoking a setting in `dealloc` can have unintended side effects (subclass overrides, key-value observers, etc). http://stackoverflow.com/questions/4580684/common-programming-mistakes-for-objective-c-developers-to-avoid/4580739#4580739 – Dave DeLong Jan 03 '11 at 16:21
  • For more information, see the Note here: http://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ObjectiveC/Articles/ocProperties.html#//apple_ref/doc/uid/TP30001163-CH17-SW16 (and the second bit about synthesized ivars not being available is incorrect; a bug has been filed to fix that) – Dave DeLong Jan 03 '11 at 16:24
  • My recommendation for setting properties to nil in dealloc was specifically limited to "(retain) synthesized properties." The docs and clang compiler both make a point to remind you that you can never provide a compatible equivalent to the @synthesized atomic guarantees. I respectfully disagree with your interpretation of the documentation. The common argument against my claim is that subclassers could change the behavior of your setter, but, in this case (declaring @property), that would be a misstep on the part of the subclasser, not the superclass dealloc. (Use KVO on self for that.) – ipmcc Jan 04 '11 at 12:40
1

Whenever you have any instance variable that is an object, you have to release it in the dealloc method. So in your case you have to use

- (void)dealloc
{
    [myArray release], myArray = nil;

    [super dealloc];
}
David
  • 13,850
  • 20
  • 90
  • 143
1

Setting to nil in theory should have the same effect as releasing the ivar. You should never call the dealloc method directly however. In the dealloc method you sometimes see the idiom mentioned by David, specifically:

[myArray release], myArray = nil;

The reason for this is to avoid a very unlikely race condition where someone might try accessing the released object before dealloc completes. By assigning the property to nil this allows the attempted access to fail gracefully.

ennuikiller
  • 43,779
  • 13
  • 108
  • 136