RandomPossessions "fast enumeration" memory management Q


#1

I’m going through Chapter 3 on Memory Management, which explains how to fix the memory leaks from the RandomPossessions application.

I really like the alternative “fast enumeration” looping example from the previous chapter, on pg 43, which uses this the “for…in” syntax, rather than the “for (i = 0; i < [items count]; i++)” syntax.

From page 43, I’m using this fast enumeration looping code:

for (Possession *item in items) { NSLog(@"%@: ", item); }

If I use the fast enumeration technique, do I need to worry about the "Possession *item line, in regards to memory management?

In the above example code, do I have have to release “item”, which is an instance of Possession ("Possession *item), since I assume the “item” variable instance is being created for the loop in order for it to iterate through the items array, but I’m unclear if “item” will automatically be released after the loop ends.

I’m assuming that “item” only exists inside the scope of the for loop, just like the “i” variable instance in the regular for loop syntax, (i = 0; i < [items count]; i++), and only exists for the duration and scope of the loop and therefore does not need to be explicitly released.

I guess in short, are all the variables (like, Possession *item) that are declared in the “for” line, automatically destroyed/released after the loop ends, and one does need to explicitly release them?

Anyway, just wondering and looking for clarification.

BTW, this is a great chapter on Memory Management. Really cleared some things that were fuzzy for me previously. I like the explanation of malloc and C memory usage as it helped me to understand the differences in Objective-C a lot better.


#2

To understand this, you need to need to differentiate between pointers to objects and objects.

Objects are instances of a class and are simply a chunk of memory that exists on the heap. An object’s size is dictated by its class - generally speaking, the more instance variables a class declares, the larger an instance of that class will be.

Variables that are pointers to objects and are declared in the body of method or function, like item, store their memory on the stack. The size of a pointer variable is determined by various platform properties (hardware, OS, etc.). Currently on the iPhone, a pointer variable is always four bytes, regardless of the type of object it is pointing to.

We only need to manage the memory for objects on the heap. The memory for a variable on the stack is automatically freed when the scope it was declared in runs out. In the case of this fast enumeration for loop, a 4 byte pointer variable is created at the beginning of each iteration of the for loop and destroyed at the end of that iteration.

Here is the important bit, though: Before you ever hit this for loop, the memory for all of the actual Possession instances are actually sitting on the heap - they already exist. These objects will stay in memory as long as at least one other object retains ownership of them (right now, the items array owns all of them). By creating the temporary pointer variable, item, you do not create another Possession instance. You are simply storing the location of an already existing Possession instance into what is essentially a temporary variable, item.

So, we can say that the following declaration in the body of a method creates a pointer variable but does not create an object:

and that objects are only truly created when we see an alloc or copy message:

[[Possession alloc] init];
[object copy];

#3

Aha!!! :smiley:


#4

I was going to ask the same question!

And while I understand Joe’s response, I’d like to just just make sure I’m clear on one aspect…

Would it be correct to say that that using a pointer in this way is more the exception than the rule? That is, that it’s normally best practice to use retain when instantiating a new pointer (to point to an existing object) because one can’t be certain that the object won’t be released and deallocated by another process/method?

Obviously in this very simple example we know that the object will be retained by the array throughout the execution of the loop, as the array must continue to exist for the loop to continue to execute. However I’m thinking it would be bad practice to make this assumption in other, more complex scenarios (where one might be tempted to make the assumption!).

Am I beating a dead horse here, or do you see what I’m getting at?


#5

Put it this way, if you are given a pointer to an object, you really have two choices: you will only send messages to that object within the scope of the method you got that pointer in; or you will send messages to that object in another method, some time later.

If the first one is true, you don’t need to retain it, another method is not going to be called while your method is running (in a single threaded environment, but multi-threading is a whole 'nother issue). If the second one is true, you must retain it.


#6

I haven’t read the whole thread, so I’m not familiar with the context of this issue, but make sure you don’t already have a variable in scope called item. In the fast enumeration, try for(Possession *thisItem in items) to rule that out. Just a thought.


#7

You haven’t included the complete code after the change you made, so if I take your description literally I imagine it would look like this:

[code]for(int i=0; i<100; i++){

for (int i =0; i<20; i++){
    [items addObject:[Possession randomPossession]];
}
 
for (Possession *item in items) {
    NSLog(@"%@", [items objectAtIndex:i]);
}

}[/code]

If that’s the case, then you will be NSLogging [items objectAtIndex:0] for [items count] times, then [items objectAtIndex:1] for [items count] times, and so forth, because you’re still using i as the index, and i is not changing in that fast enumeration loop. That would explain the output you’re seeing. Instead, change that block to:

    for (Possession *item in items) {
        NSLog(@"%@", item);
    }

Try to avoid using the same variable name for your nested loops. It can get you into trouble like this. Using unique names would have given you a compiler error and led you to the problem.

Adam