With Objective-C Cocoa, we're working with semi-automatic reference-counting memory management. When allocating memory for an object, retaining an object, or calling a copy
method on an object, the retain count (reference count) increments by 1. When calling release
on an object, it decrements the retain count by one. When calling autorelease
on an object, release
will be called on the object at some point in the future (during the main run loop, when none of your own code is executing, so it won't pull the reference out from under you as you're trying to use it). When the retain count reaches 0, the object can be deallocated.
In general, if you're calling retain
on an object, you're signalling your interest in it, and you are responsible for making a release
or autorelease
call at some point when you're no longer interested in the object. Likewise, if you call alloc
or a copy
method on an object, you have signalled your interest in the object and must match it with a release
or autorelease
somewhere down the line.
This link pretty much covers the guidelines Apple uses (and you should use) for memory management: Simple rules for memory management in Cocoa
Let's go through the code line by line:
ClassOne *pointer = [[ClassOne alloc]init];
pointer
points to a newly allocated ClassOne object, with a retain count of 1, since we called alloc on it. We have a responsibility to call release
or autorelease
on pointer
at some point in the future.
ClassTwo *foo = [[ClassTwo alloc]init], *foo2;
foo
points to a newly allocated ClassTwo object, with a retain count of 1, since we called alloc on it. We have a responsibility to call release
or autorelease
on foo
at some point in the future.
foo2
doesn't point to anything in particular right now. It's not safe to use.
foo2 = [foo add: pointer];
pointer
has been added to foo
(whatever that means; we don't know the implementation). foo
might have called retain
on pointer
to signal its interest in it, and added it as a field, or it might have added pointer
to a collection (in which case it's the collection's responsibility to call retain
on it when an object is added, and release
when an object is removed). In any case, it doesn't affect our code block, so we don't care what's going on under the hood
The reference returned by this method might be pointer
itself, or it might be an autoreleased copy of pointer
; we don't have access to the API or the implementation to tell us which.
In either case, it is not our responsibility to call release
on this object. If the method had copy
in the name, or if we had called retain
on the returned reference (like foo2 = [[foo add:pointer] retain];
), then the retain count would have been incremented by 1, and it would have been our responsibility to call release
or autorelease
on it.
[foo release];
The object referenced by foo
has been released, meaning its retain count has been decremented by 1. For this example, this pairs with the alloc
call we made in line 2, so the retain count will drop to 0, making foo
eligible to be freed.
In general, though, we don't care if the object has been deallocated or not; we just need to make sure we pair up any alloc
, copy
, or retain
calls with the same number of release
or autorelease
calls. If we register an interest in an object at any time, it's our responsibility to release our interest, otherwise we'll have memory leaks.
foo = foo2;
foo
now points to the same object referenced by foo2
. Remember, we haven't called an alloc
or copy
method when we got foo2
, nor did we register an interest in it by calling retain
. Since we don't have a responsibility to call release
on foo2
, we don't have a responsibility to call release
on foo
.
[pointer release];
pointer
's retain count has been decremented by 1. This may have brought its retain count to 0 or not, it depends on what foo
did with it when we added it. Still, we don't care; we have finished our responsibility to pointer
by calling release
on it to match with the alloc
call we made at the beginning. Although pointer
might still be around after this call, we can't make that assumption, and trying to do anything with the object previously referenced by pointer would be a mistake (though we could change pointer
to point at something else freely).
[foo release];
If the author of this code has been following Apple's memory management conventions, then this is unnecessary. We don't have a responsibility to call release
on foo
or foo2
(they point to the same object, remember). This won't cause the code to break; calling anything on a nil
reference is essentially a no-op. However, it may cause confusion for anyone reviewing the code.
Now, the author of this code may have broken the memory management conventions. He might have made that add
call return a copy of pointer
without calling autorelease
on it, in which case it makes the caller responsible for calling release
on it. This is very bad form, and if you should run into code which breaks the memory management convention, document where you use it and how it breaks the convention to avoid confusion in the future.