Search code examples
objective-cmemory-leaksclass-method

Memory leak caused by alloc in Class Method?


I have found that if I alloc a new object inside a Class method and return it to main() it seems to cause a memory leak when I no longer want the object.

For example, here is a simple Class that includes a Class method that returns an instance of itself:

@interface Stinker : NSObject
{
    int a;
}
+(instancetype) makeAStink;
-(void) showThem;
-(void) setEm: (int) z;
@end

@implementation Stinker
-(void) showThem
{
    NSLog(@"%d",a);
}

-(void) setEm: (int) z
{
    a = z;
}

-(void) dealloc
{
    NSLog(@"Arrrgggggh!");
}

+(instancetype) makeAStink
{
    id temp = [[self alloc] init];
    return temp;
}
@end

Now if I create an instance directly from main():

Stinker *aStink =[[self alloc] init];

and subsequently set aStink to nil:

aStink = nil;

the overridden dealloc method is called and the Argggggh! message is logged. That's fine and as expected.

But if I use the Class method I wrote to create an instance:

Stinker *aNewStink = [Stinker makeAStink];

the behaviour is different.

Now if I set aNewStink to nil, it will no longer point to the object but the object is not destroyed. dealloc is not called and the Arggggh message is not logged.

It seems like it still has an owner somewhere.

Of course when main() terminates the object is destroyed and dealloc is eventually called. But this seems to suggest that unused and unloved objects are still hanging around on the heap until the program terminates.

Isn't this a memory leak? Should I just avoid using Class methods to alloc new instances?


Solution

  • When using ARC, the following code

    +(instancetype) makeAStink
    {
        id temp = [[self alloc] init];
        return temp;
    }
    

    will be same with Non-ARC like this:

    +(instancetype) makeAStink
    {
        id temp = [[self alloc] init];
        return [temp autorelease];
    }
    

    Thanks to autorelease, aNewStink = nil will make aNewStink do release in next runloop.

    So if you do this:

    @autoreleasepool {
      Stinker *aNewStink = [Stinker makeAStink];
      aNewStink = nil;
    }
    

    Dealloc method is called immediately.