PDA

View Full Version : Obj-C questions - vars and memory mgmt




GregX999
Apr 5, 2009, 11:12 AM
I have two quick (hopefully) questions related to variables/pointers and memory management in Objective-C.

Take the following example:

for(i=0; i<10; i++){
NSString *greeting = @"Hello @%", [someArrayOfNames objectAtIndex:i];
// do something with greeting
}

Does this code create 10 separate NSString objects? And if so, is it the case that each time through the loop, the variable "greeting" points to the one just created, and older ones aren't (easily) accessible anymore?

Thanks for any help! I'm still trying to get pointers and memory management down.

Greg



Saladinos
Apr 5, 2009, 12:05 PM
If you have the garbage collector enabled (which you should do), you don't need to worry. After each iteration of the loop, the old NSString instance is released. This is because it was defined in the scope of the loop iteration, which has ended, so there is no way for anything to access that variable. Hence, it is collected by the GC.

mdeh
Apr 5, 2009, 12:34 PM
I have two quick (hopefully) questions related to variables/pointers and memory management in Objective-C.

Take the following example:

for(i=0; i<10; i++){
NSString *greeting = @"Hello @%", [someArrayOfNames objectAtIndex:i];
// do something with greeting
}

Does this code create 10 separate NSString objects?
......
snip
'.......
Greg

No, your code does not create NSString objects...all it does is point *greeting to a new object each time through the loop.

Look at this link...it will help you

http://www.stepwise.com/Articles/Technical/2001-03-11.01.html

GregX999
Apr 5, 2009, 12:35 PM
If you have the garbage collector enabled (which you should do), you don't need to worry. After each iteration of the loop, the old NSString instance is released. This is because it was defined in the scope of the loop iteration, which has ended, so there is no way for anything to access that variable. Hence, it is collected by the GC.

Thanks. I know about the auto-GC, but I'm trying to learn the manual way as well. I have dreams of programing for the iPhone - it doesn't support GC.

Greg

lee1210
Apr 5, 2009, 12:36 PM
Memory management issues aside, that code doesn't do what you think it does.

You have an expression with a comma operator. The expression on the left side of the comma operator will be evaluated ("How nice, the NSString literal 'Hello @%', i will gladly ignore that thing"), then the right side will be evaluated (grabbing, presumably, an NSString * from an NSArray), and assign that thing on the right to greeting. So greeting is just going to be the value from the array (say, "xyz"), not "Hello xyz" where xyz is an element of the array as you're hoping. In terms of memory management, this loop has zero impact. The NSString literal lives forever, so no memory management issue there. Assigning members of the array won't be much of an issue, either.

What you intend to do, I assume, is use NSString's:
+ (id)stringWithFormat:(NSString *)format, ...
method, and get a new string. it would look something like:

for(NSString *singleName in someArrayOfNames) {
NSString *greeting = [NSString stringWithFormat:@"Hello %@",singleName];
//...
}


In this case, if you have an autorelease pool in place (and you should), you still don't need to worry. stringWithFormat will return an autoreleased object.

-Lee

GregX999
Apr 5, 2009, 09:55 PM
Thanks Lee!

I figured the stringWithFormat thing out earlier today. Yes, what I intended was exactly what you wrote.

I'm getting a much better understanding of the autorelease stuff too.

While playing with some code, I see something a bit strange (to me)...

Why does the following line

NSString *greeting = [NSString stringWithFormat:@"Hello %@",singleName];

not trigger an error about trying to redeclare "greeting" when inside a for-loop, but it will trigger such an error if just placed sequentially, one after the other?

Does being inside the for-loop make the variable "local" to the for-loop?

Greg

lee1210
Apr 5, 2009, 10:05 PM
Thanks Lee!
<snip>
Does being inside the for-loop make the variable "local" to the for-loop?

No problem.

Yes, exactly. Every block ({}) has scope. This includes functions, loop structures, etc. I just dug up this code that I posted in another thread, and added one block that isn't part of any other structure:

#include <stdio.h>
int x = 12;

int main(int argc, char *argv[]) {
int x = 0;
x = 7;

if(x > 6) {
int x;
printf("x is: %d\n",x); /*Uninitialized, who knows?*/
x = 2;
printf("x is: %d\n",x); //prints inner-scoped x, 2
}
printf("x is: %d\n",x); //prints outer-scoped x, 7

{
int x = 17;
printf("X in last block is: %d\n",x);
}
return 0;
}


Not only can you declare new variables, but you can mask them by using the same name in a block with "lower" scope.

-Lee

gnasher729
Apr 6, 2009, 06:43 AM
Not only can you declare new variables, but you can mask them by using the same name in a block with "lower" scope.

Note that most of the time it is not a good idea to do this, and most of the time when you see it, it is actually a mistake of the programmer. (And I know Lee didn't say it was a good idea, he was just explaining why it wasn't a compiler error).

lee1210
Apr 6, 2009, 07:05 AM
Note that most of the time it is not a good idea to do this, and most of the time when you see it, it is actually a mistake of the programmer. (And I know Lee didn't say it was a good idea, he was just explaining why it wasn't a compiler error).

In my heart I have faith that after seeing that abomination, with 4 x's with different scope, with up to 3 "in-flight" at once, one would feel sick to their stomach and never want to do something like that. Since we do have beginners on the board, though, it is worth stating explicitly that this is poor style, and should be avoided as gnasher729 pointed out.

-Lee

GregX999
Apr 6, 2009, 12:02 PM
Wow, I didn't realize that - good to know!

Does this apply for objects/pointers too? In this example, would "person" exist or point to anything after the loop exits (the last person created)?


Crowd *crowd = [[Crowd alloc] init];
for (NSString *name in nameArray){
Person *person = [[Person alloc] initWithName: name];
[crowd addPerson: person];
}


Greg

autorelease
Apr 6, 2009, 01:04 PM
Wow, I didn't realize that - good to know!

Does this apply for objects/pointers too? In this example, would "person" exist or point to anything after the loop exits (the last person created)?


Crowd *crowd = [[Crowd alloc] init];
for (NSString *name in nameArray){
Person *person = [[Person alloc] initWithName: name];
[crowd addPerson: person];
}


Greg

The data pointed to by person (i.e. the object) would still exist since it's been allocated on the heap. However, the actual pointer itself would go out of scope when the loop exits like any other variable.

lee1210
Apr 6, 2009, 03:17 PM
Wow, I didn't realize that - good to know!

Does this apply for objects/pointers too? In this example, would "person" exist or point to anything after the loop exits (the last person created)?


Crowd *crowd = [[Crowd alloc] init];
for (NSString *name in nameArray){
Person *person = [[Person alloc] initWithName: name];
[crowd addPerson: person];
}


Greg

autorelease basically covered this, but the difference in this case vs. the stringWithFormat: case is that stringWithFormat: doesn't have alloc, new, or copy in it, which is the way Cocoa methods are named when they return an object that you own*, vs. an autoreleased object. initWithName: does have init in it, so you own the object at the other end of the Person *. You lose your pointer to the Person created during each loop iteration, so you MIGHT leak memory. However, you're adding each Person to your Crowd. If you handle releasing all of the Persons in the Crowd before "Removing" a person, and when the Crowd itself if released, then this could be OK.

As for the Person *, its scope is just the for loop, so those 4 or 8 bytes are no more after the } of the for loop.

-Lee

* Annotation: http://developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmObjectOwnership.html#//apple_ref/doc/uid/20000043

GregX999
Apr 7, 2009, 11:23 AM
autorelease basically covered this, but the difference in this case vs. the stringWithFormat: case is that stringWithFormat: doesn't have alloc, new, or copy in it, which is the way Cocoa methods are named when they return an object that you own*, vs. an autoreleased object. initWithName: does have init in it, so you own the object at the other end of the Person *. You lose your pointer to the Person created during each loop iteration, so you MIGHT leak memory. However, you're adding each Person to your Crowd. If you handle releasing all of the Persons in the Crowd before "Removing" a person, and when the Crowd itself if released, then this could be OK.

Ok, so it seems like I really should use autorelease when I create each person...

Person *person = [[[Person alloc] initWithName: name] autorelease];

...so that if the dealloc method of crowd releases all the persons (by releasing the people NSArray), all the person objects will go away and no memory will be leaked. Yes?

Greg

lee1210
Apr 7, 2009, 12:50 PM
that is a possible approach. Another would be to make a +personWithName: method for Person, and have it return an object that is already autoreleased. It would make the code that uses it cleaner (rather than sending autorelease every time you get a person).

Note that if it is a long time before your pool is drained, the memory will still be in use. This may not be a big deal, but just something to consider. You can always set up a new autorelease pool with a shorter life that will release autoreleased things sooner when it is drained.

-Lee