Help with NSMutableArray memory leak in a loop

Discussion in 'Mac Programming' started by bluesky2000, Oct 2, 2008.

  1. bluesky2000 macrumors newbie

    Joined:
    Sep 18, 2008
    #1
    Hi all, i'm fairly new to Cocoa and need help with memory leak. I was testing a very simple application. There is only one button and when the button is clicked , it calls buttonClicked method then calls populateArray method. It looks like this.


    - (IBAction) buttonClicked: (id) sender
    {
    //g_array is a public variable
    //[g_array release]; // still leaks
    g_array = [[NSMutableArray alloc] init];
    [self populateArray];
    }

    - (void) populateArray
    {
    int i;
    for (i = 0; i < 10; i++)
    {
    NSString *str = [NSString stringWithFormat: @"%i", i];
    //NSString str = [[NSString alloc] initWithString:mad:"test"];
    [g_array addObject: str];
    //[str release];
    }
    }


    - (void)dealloc
    {
    // Release Array
    [g_array release];
    }


    If i click the button first few times, it doesn't leak. But after several clicks, it leaks. What would be the proper way to reuse public NSMutableArray variable? Should initialization of array go into awakeFromNIB instead? Thanks.
     
  2. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #2
    Where you put the alloc and init for g_array is up to you. awakeFromNib would be a fine place to do it. What's happening now is that each time you click the button, and buttonClicked is called, a new NSMutableArray is allocated, initialized, and the pointer is assigned to g_array.

    The old NSMutableArray g_array was pointing to is no longer accessible from your code, as you've gotten rid of the only pointer you had. This means you can't call release on it, so it's retain count is still non-zero, and it will never get freed. You are leaking as soon as the button is clicked a second time. I'm not sure what you're using to detect the leak, but it may be a little behind in realizing you've lost all references to an object that still has a non-zero retain count.

    You could send release to g_array each time the button is clicked instead of moving the allocate. The first time it should be nil, and the message is a no-op. Each additional time, you will decrement the retain count of the old array and allocate a new one.

    If you do move the alloc and init elsewhere, since you're using a mutable array, in buttonClicked you could just call removeAllObjects: then call populateArray. Otherwise you would continue to add an additional 10 objects with each click.

    -Lee
     
  3. bluesky2000 thread starter macrumors newbie

    Joined:
    Sep 18, 2008
    #3
    Thanks for your quick reply. So do you mean like this?

    - (void)awakeFromNib{
    g_array = [[NSMutableArray alloc] init];
    }

    - (IBAction) buttonClicked: (id) sender {
    [g_array removeAllObjects];
    [self populateArray];
    }

    - (void) populateArray {
    int i;
    for (i = 0; i < 10; i++) {
    NSString *str = [NSString stringWithFormat: @"%i", i];
    [g_array addObject: newCard];
    }
    }

    - (void)dealloc {
    [g_array release];
    }



    Actually I used [g_array release] before alloc g_array in buttoneClicked method. But that still leaked. I also used similar code like above but still leaked. Maybe I was too tired last night. Can't wait to go home and test the code.
    BTW I used objectAlloc instrument to track memory leak.
     
  4. kainjow Moderator emeritus

    kainjow

    Joined:
    Jun 15, 2000
    #4
    Also don't forget to call [super dealloc]; in your dealloc method.
     
  5. garethlewis2 macrumors 6502

    Joined:
    Dec 6, 2006
    #5
    You should for Cocoa code, put any initialization code that does not actually pertain to interface code, e.g. buttons, tables, etc in the init method. While you can put it in the awakeFromNib, it's not actually a good habit to get into.
     

Share This Page