Obj-C questions - vars and memory mgmt

Discussion in 'Mac Programming' started by GregX999, Apr 5, 2009.

  1. macrumors newbie

    GregX999

    Joined:
    Apr 5, 2009
    #1
    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
     
  2. macrumors 68000

    Saladinos

    Joined:
    Feb 26, 2008
    #2
    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.
     
  3. macrumors 6502

    Joined:
    Jan 3, 2009
    #3
    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
     
  4. thread starter macrumors newbie

    GregX999

    Joined:
    Apr 5, 2009
    #4
    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
     
  5. macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #5
    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:
    Code:
    + (id)stringWithFormat:(NSString *)format, ...
    method, and get a new string. it would look something like:
    Code:
    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
     
  6. thread starter macrumors newbie

    GregX999

    Joined:
    Apr 5, 2009
    #6
    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:mad:"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
     
  7. macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #7
    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:
    Code:
    #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
     
  8. macrumors G5

    gnasher729

    Joined:
    Nov 25, 2005
    #8
    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).
     
  9. macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #9
    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
     
  10. thread starter macrumors newbie

    GregX999

    Joined:
    Apr 5, 2009
    #10
    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)?

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

    Greg
     
  11. macrumors regular

    Joined:
    Oct 13, 2008
    Location:
    Achewood, CA
    #11
    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.
     
  12. macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #12
    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/document...ctOwnership.html#//apple_ref/doc/uid/20000043
     
  13. thread starter macrumors newbie

    GregX999

    Joined:
    Apr 5, 2009
    #13
    Ok, so it seems like I really should use autorelease when I create each person...

    Code:
    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
     
  14. macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #14
    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
     

Share This Page