views:

4194

answers:

9

Hi, 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];
+1  A: 

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.

DannySmurf
+3  A: 
ClassOne *pointer = [[ClassOne alloc]init];

The variable pointer is a pointer to an object of class ClassOne. It it assigned the value of a newly created object.

ClassTwo *foo = [[ClassTwo alloc]init], *foo2;

*foo and *foo2 are objects of class ClassTwo. Only foo is assigned a newly created object. foo2 may point to anything, so it is unsafe to use it before assigning it a value.

 foo2 = [foo add: pointer];

foo2 is assigned a value: I assume that the add: message of class ClassTwo creates an object (the signature of the method should be -(ClassTwo*)add:(ClassOne*);)

 [foo release];

The object pointed to by foo is no longer needed.

 foo = foo2;

The variable foo is assigned the value of foo2: both point to the same object.

[pointer release];

The object pointed to by pointer is no longer needed.

[foo release];

The object pointed to by foo (and also by foo2) is no longer needed.

mouviciel
Actually, the signature of the method should be - (ClassTwo*) add:(ClassOne*); -- in his/her example, the receiver is an instance of ClassTwo, not the class itself.
Jason Coco
Yes, you're right! I update my answer, Thank you.
mouviciel
+1  A: 

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?

A: 

That really depends on what [foo add:pointer]; does. It looks like it gives back a copy of foo 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 named add: shouldn't give back a copy.

Step by step:

// this somehow creates a retained copy of foo.
foo2 = [foo add:pointer];

 // foo is released and gets destroyed.
[foo release];

// makes foo point to the same object as foo2
// (`foo` has no connection to the former object anymore)
foo = foo2;

// foo (and foo2 as they point to the same object) are released
[foo release];
Georg
A: 

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.

Georg
+13  A: 

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.

The [foo release] at the end probably *will* cause a crash if we're following the memory-management guidelines. It's set to the result of [foo add:pointer], which we don't know is nil. If it's anything other than nil, program go boom.
Chuck
+1  A: 

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!

archieoi
A: 

@arceioi

Pls give me a responce,

I too have the same doubt about your question. I believe here foo2 must be a copy of foo, since foo is released and then foo2 is assigned to foo. If foo2 was a reference of foo, when foo was deleted foo2 must also have got dereferenced...?

Ya i know i sound to be naive . Pls Help.

Sumit M Asok
A: 

For a good example of a class that automatically releases all the retained objects in your class, have a look at example 5 here:

http://www.modejong.com/iPhone/

cheers Mo

MoDJ