Quick memory management question

Discussion in 'iOS Programming' started by tod, May 22, 2010.

  1. tod macrumors regular

    Joined:
    Oct 3, 2009
    Location:
    Ohio
    #1
    I have a quick memory management question. Here is the code:

    Code:
    NSArray *keys = [[[NSArray alloc] initWithObjects: @"1", @"2",nil] autorelease];	
    	
    NSMutableArray * temp = [[NSMutableArray alloc] init];
    	
    temp = [NSMutableArray arrayWithArray:keys];
    	
    [temp release];
    When I release the temp object, the app crashes. If I remove that line, it runs, but of course it will leak memory.

    I've been looking through the documentation for hours and I've looked at countless examples and I can't figure out why this crashes. I alloc an object, do something with it, then release it. Everything I've written conforms to the documentation, as far as I understand it. What am I missing here?
     
  2. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #2
    What are you "doing" with it other than reassigning it right away, which actually throws out what you just alloc'd? That is what is causing the leak.
     
  3. kainjow Moderator emeritus

    kainjow

    Joined:
    Jun 15, 2000
    #3
    Code:
    NSMutableArray * temp = [[NSMutableArray alloc] init];
    Temp has a retain count of 1 at this point.

    Code:
    temp = [NSMutableArray arrayWithArray:keys];
    Now temp has been assigned to a new autoreleased object. Meaning, the object gets created with a retain count of 1, but will be released at a future later point which means you should not release it.

    Code:
    [temp release];
    By calling release here, you set the retain count to 0, which causes temp to deallocate.

    The crash you see comes from the future point in time when the autorelease pool empties. It contains a reference to temp which is no longer a valid object (pointer), which causes the crash.

    I would suggest reviewing:
    - C pointers
    - Cocoa memory management, specifically the section on autorelease pools
     
  4. tod thread starter macrumors regular

    Joined:
    Oct 3, 2009
    Location:
    Ohio
    #4
    Thank you. This is very helpful. For anyone reading this in the future, here is what I was thinking:

    I understood that in following code

    Code:
    temp = [NSMutableArray arrayWithArray:keys];
    [NSMutableArray arrayWithArray:keys] is an autoreleased object that I don't have to worry about. I thought that I was assigning that autoreleased object to temp (which I had ownership of, and thus could dispose of later). So in my mind, it was like this: set [my object with a retain count of 1] to be equal to [this convenient, autoreleased object];

    Much of the discussion I found centered around retain and release counts, which I had no problem with as long as I didn't use convenience methods. I had no idea that temp would become autoreleased in that case.
     
  5. admanimal macrumors 68040

    Joined:
    Apr 22, 2005
    #5
    As kainjow pointed out, it's important the understand the distinction between the pointer (temp) and the object it is pointing to. When you do this:

    Code:
    NSMutableArray * temp = [[NSMutableArray alloc] init];
    
    You are basically saying, "give me a new block of memory with an NSMutableArray in it" and "make temp point to this memory." The NSMutableArray that you just created will have a retain count of 1. The "retain count" of temp is entirely dependent on the object it is pointing to.

    Then when you do this:

    Code:
    temp = [NSMutableArray arrayWithArray:keys];
    
    You are now saying, "give me an autoreleased NSMutableArray in a new block of memory" and "switch temp so that it points to this new memory." The key thing here is at this point you have now created two distinct NSMutableArrays, each living in their own blocks of memory with their own distinct retain counts. Temp only knows about the retain count of the object it is currently pointing to, so when you reassign temp to the autoreleased array, you are losing any reference to the first array and its retain count, which is a memory leak. If you want to assume ownership of an autoreleased object, you need to do this:

    Code:
    NSMutableArray *temp;
    temp = [NSMutableArray arrayWithArray:keys];
    [temp retain];
    
    <do some stuff with temp>
    
    [temp release];
    
    I put the declaration of temp on its own line just to be clear that there was no prior assignment to it in the code. Of course if this code was all happening in one method, there probably would be no need to retain temp.
     
  6. firewood macrumors 604

    Joined:
    Jul 29, 2003
    Location:
    Silicon Valley
    #6
    That release isn't leaking memory, so you can remove it.

    However, your second assignment to the variable "temp" is leaking memory, so you should release it's previous contents before the (re)assignment.

    My rule of thumb is to never manually assign anything to an object pointer directly unless it is nil, or, within clear view above in the source code above, either uninitialized or assigned to an autoreleased object. Else do something to make it nil without leaking first (which is what property setters do for you automatically, but not when doing a manual direct assignment).
     
  7. TiberiusXavier macrumors member

    Joined:
    Apr 18, 2010
    Location:
    Chicago
    #7
    Sorry to redirect the discussion slightly, but I have a followup question for the gurus here.

    If the NSArray * keys is autoreleased, is that object still being retained when assigned to the new NSMutableArray temp? In other words, are the elements within the NSArray being retained or will this new temp object contain elements flagged for autorelease?
     
  8. admanimal macrumors 68040

    Joined:
    Apr 22, 2005
    #8
    There are two different things that you are talking about that could potentially be autoreleased. One is the collection of objects in the keys array, and the other is the keys array itself. Calling NSMutableArray arrayWithArray: creates a new, autoreleased array containing the objects in the given array. So if the keys object was autoreleased, it could and would just get freed eventually. On the other hand, the objects in the keys array that also get put in the temp array will be retained by the temp array (increasing their retain count to 2 at that point) so they will persist even when keys is (auto)released and the objects get released by keys.

    Or to put it another way, calling autorelease on an array technically won't autorelease the objects it contains. Those objects will still be retained by the array until the array is released by the autorelease pool and dealloc'ed, at which point the objects it contains will be released.
     
  9. TiberiusXavier macrumors member

    Joined:
    Apr 18, 2010
    Location:
    Chicago
    #9
    Thanks admanimal. I suspected it was a deep retain (I don't know the actual nomenclature so I am borrowing the deep copy term).
     
  10. admanimal macrumors 68040

    Joined:
    Apr 22, 2005
    #10
    I think the correct term is "strong reference." This would be in contrast to a weak reference where you just have a pointer to something but do not retain it.
     

Share This Page