I am new to coding and trying to get up to speed with Objective-C. Came across some code I did not understand. I was hoping someone could clarify it for me. In the case below, I am not sure how *foo2 is working and why it is not being released?
ClassOne *pointer = [[ClassOne alloc]init];
ClassTwo *foo = [[ClassTwo alloc]init], *foo2;
foo2 = [foo add: pointer];
[foo release];
foo = foo2
[pointer release];
[foo release];
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 callingrelease
on an object, it decrements the retain count by one. When callingautorelease
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 arelease
orautorelease
call at some point when you're no longer interested in the object. Likewise, if you callalloc
or acopy
method on an object, you have signalled your interest in the object and must match it with arelease
orautorelease
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:
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 callrelease
orautorelease
onpointer
at some point in the future.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 callrelease
orautorelease
onfoo
at some point in the future.foo2
doesn't point to anything in particular right now. It's not safe to use.pointer
has been added tofoo
(whatever that means; we don't know the implementation).foo
might have calledretain
onpointer
to signal its interest in it, and added it as a field, or it might have addedpointer
to a collection (in which case it's the collection's responsibility to callretain
on it when an object is added, andrelease
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 hoodThe reference returned by this method might be
pointer
itself, or it might be an autoreleased copy ofpointer
; 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 hadcopy
in the name, or if we had calledretain
on the returned reference (likefoo2 = [[foo add:pointer] retain];
), then the retain count would have been incremented by 1, and it would have been our responsibility to callrelease
orautorelease
on it.The object referenced by
foo
has been released, meaning its retain count has been decremented by 1. For this example, this pairs with thealloc
call we made in line 2, so the retain count will drop to 0, makingfoo
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
, orretain
calls with the same number ofrelease
orautorelease
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
now points to the same object referenced byfoo2
. Remember, we haven't called analloc
orcopy
method when we gotfoo2
, nor did we register an interest in it by callingretain
. Since we don't have a responsibility to callrelease
onfoo2
, we don't have a responsibility to callrelease
onfoo
.pointer
's retain count has been decremented by 1. This may have brought its retain count to 0 or not, it depends on whatfoo
did with it when we added it. Still, we don't care; we have finished our responsibility topointer
by callingrelease
on it to match with thealloc
call we made at the beginning. Althoughpointer
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 changepointer
to point at something else freely).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
onfoo
orfoo2
(they point to the same object, remember). This won't cause the code to break; calling anything on anil
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 ofpointer
without callingautorelease
on it, in which case it makes the caller responsible for callingrelease
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.Wow, Thank you all for the great responses!
I guess what I am really meant is a reference to an object and not a pointer. Nonetheless I guessing the appended
, *foo2
resides in the same memory as foo. Also foo2 is released form memory at the same time as foo. I still have a lot more to lean but one day at a time!Because you haven't released it. You're releasing references here, not freeing pointers. It's not quite the same thing. When you do [foo release] the second time, you're releasing the foo reference that you created when you assigned foo2 to foo.
To release the foo2 reference, you need to actually call release on that reference, not a copy of the reference.
That really depends on what
[foo add:pointer];
does. It looks like it gives back a copy offoo
and retaining it. This is clearly bad design because it should be obvious from the method if the returned object is a copy / reference. Methods with namedadd:
shouldn't give back a copy.Step by step:
From your simple example it is really hard to say what is going on. Typically [class add:] type methods return void, so they should raise a compiler warning that 'void value is not ignored as it should be.'
So without more info, it is a bit hard to figure things out.
A few things to keep in mind:
you can send commands to 'nil' in objc. So, if [foo add:pointer] returns nil, then you can call 'release' on it all day with no affect.
retainCount is your friend. You can call it on any NSObject to see how many objects are holding onto it. This may also help you track down the issue.
Finally, is Garbage Collection on?
What you really try to focus on is how pointers work. I think you haven't yet understood the difference between pointers and objects.
Here's the link to pointers on wikipedia.