views:

60

answers:

1

I've spent about 10 hours trying to find this bug that was causing my app to crash, and it was in the last place I looked (well it would have been, but last place I ever expected it to be).

Originally I thought I had memory management issues (unbalanced retain/release) because the crash would happen every time I sent -removeAllObjects to an NSMutableArray filled with my custom objects. The crash wouldn't happen the first time -removeAllObjects was called. I could clear the array once, repopulate it, and then on the second clear, I would get a EXC_BAD_ACCESS. This is when my array got populated with 3 objects on the first "cycle", and 3 again on the second "cycle". When I was storing only 1 object in the array in each cycle it took 4 cycles to crash (on the 4th call of -removeAllObjects).

I FINALLY realised that the crash would go away if I changed the -init method of my custom object. Here's the -init implementation; all 4 ivars are synthesized properties with (nonatomic, retain), all of type (NSString *) except for icon which is an (NSNUmber *)

-(id)init {
    if (self = [super init]) {
        ip = @"";
        mac = @"";
        vendor = @"";
        icon = [NSNumber numberWithInt:0];
    }
    return self;
}

Changing it to this fixed the bug:

-(id)init {
    if (self = [super init]) {
        self.ip = @"";
        self.mac = @"";
        self.vendor = @"";
        self.icon = [NSNumber numberWithInt:0];
    }
    return self;
}

I've read that one should't use the accessors in the -init method because it can cause trouble (e.g. with subclassing).

If someone could explain to me why my bug goes away when I use accessors I would be so incredibly grateful! Seriously this has been driving me nuts, was up till 5am last night because of this.

+8  A: 

You are assigning, but not retaining, the instance variables directly. When you use the dot syntax, you are triggering the retain part of the synthesized property and, thus, are retaining them.

-(id)init {
    if (self = [super init]) {
        ip = @"";
        mac = @"";
        vendor = @"";
        icon = [[NSNumber numberWithInt:0] retain];
    }
    return self;
}

That should likely fix the problem (though, I'm slightly surprised, I thought 10 was still in NSNumber's instance cache. Maybe not.).

Technically you should also retain the @"" strings, but you can get away with not doing so because such strings are a special cased constant string that comes out of the compiled executable (as a private subclass of NSString that overrides to not respond to retain/release/autorelease).

The memory management guide covers this in detail. For anyone new to the platform, I would suggest re-reading it once a month (no, really -- interleaving your coding with an occasional re-read of the docs will often reveal subtle details that you didn't have enough experience to grok before. I still re-read the basic guides on a semi-annual basis.)

bbum
Thank you! That makes sense. How come the crash happens after a while and not instantly though? I was init'ing that object quite a few times before it crashed. As for your suggestion on re-reading, I think that's a good idea, I was actually planning on re-reading the HI and Memory Management guides once I had made a few apps and had some grounding/experience to fully appreciate them.
codenoob
codenoob: Because `numberWithInt:` may return an autoreleased object, in which case, it will be released (and thereby freed) when the autorelease comes due. Usually, this happens when you get back to the application's event loop.
Peter Hosey