views:

1064

answers:

4

I've been staring at this same issue for a long time now, and would really appreciate any help or suggestions. I'm sure its something simple, but I can't seem to find it. In my app delegate I'm loading up a bunch of accessory objects (an object I created, which supports NSCopying) with the following code:

NSString  *path   = [[NSBundle mainBundle] pathForResource:@"Accessories" ofType:@"plist"];
NSDictionary *accDict = [[NSDictionary alloc] initWithContentsOfFile:path];

self.colors = (NSArray *) [accDict objectForKey:@"Colors"]; 
self.exteriorAccessories = [self loadAccessoriesForMode:EXTERIOR_MODE withDictionary:accDict];
self.interiorAccessories = [self loadAccessoriesForMode:INTERIOR_MODE withDictionary:accDict];
[accDict release];

And this is the definition for the method its calling:

-(NSArray *)loadAccessoriesForMode:(NSString *)mode withDictionary:(NSDictionary *) dictionary
{
    NSMutableArray *tempValues = [[NSMutableArray alloc] init]; 
    for (NSDictionary *value in [dictionary objectForKey:mode])  
    {
        Accessory *accessory = [[Accessory alloc] initWithDictionary:value];
        [tempValues addObject:accessory];
        [accessory release];
    }

    NSArray *returnArray = [[NSArray alloc] initWithArray:tempValues copyItems:YES];
    [tempValues release];
    [returnArray autorelease];

    return returnArray; 
}

When I get to the release for accDict I'm getting an EXC_BAD_ACCESS exception. If I take out the release of accessory inside the loop, everything is fine - but I'm leaking Accessory objects (which seems obv. to me - if I init it and I alloc it, its my job to release it).

When I step through this in the debugger, I'm seeing the init, copy and dealloc methods all fire on my Accessory object as expected. I can also post code for the Accessory object if you think it will help, but I think the problem is somewhere in this code.

A: 

Please prefix this with an "I know nothing about objective C but":

It looks to me like you need to release the accessory items after you have copied them into the "returnArray", or maybe not specify "copyItems".

1800 INFORMATION
A: 

Run Clang on your code. It's a godsend. Clang rules! It will do Static Analysis of your code and tell you what you might be leaking. Great stuff.

Genericrich
+2  A: 

I think I've found the cause, but I'll post it here so others can possibly benefit. It didn't really have anything to do with the code I posted. Rather the problem was inside of the Accessory object. I was setting things directly instead of calling the getters through self.

So this:

value = [dict objectForKey:@"myKey"];

Instead of this:

self.value = [dict objectForKey:@"myKey"];

Somehow this was causing me to have bad side effects on the NSDictionary itself (I thought that was not mutable, but it seems I was somehow messing things up). The only way I found this was to use the very helpful advice that I found on Cocoa With Love.

When I used the Print Description option in XCode, I was able to see that the NSDictionary somehow contained AccessoryValue objects - one of my custom objects that should NOT have been there since this was just loaded from a simple plist. Print Description can be found in XCode by hovering over the object to see its details (while the process is paused in the debugger) and clicking on the little up/down arrows right next to the triangle that expands into object details. For dictionaries, this will dump their entire contents to the console.

Bdebeez
It's important to know how the property "value" is defined and what's happening in its accessor methods. Are there retains and releases going on in there?
Jason Coco
Yes, value is a property with (nonatomic, retain). So my hunch was that calling "value =" just set the pointer to that w/o calling retain, whereas "self.value =" calls the appropriate setter method, thus invoking the retain.
Bdebeez
A: 

I was battling with the Exc_Bad_Access issue for a day and finally found the answer. The issue was when I try to access one of the objects stored in an NSDictionary, the first time was OK but the second access the object turned to be nil even though the object counts in the dictionary remains the same. This strange behavior was due to releasing the object twice. Here is an example:

NSString* nstring=[[[NSString alloc]init]autorelease] [AnNSDictonaryInstance setObject:nstring forKey:0];

... [nstring release];

Notice that nstring was set autorelease then release again? It won't show problem right away unit you try to read the dictionary object the second times. I hope one day Apple's development team will be able to flag this as an violation while compiling.

I hope this post will help someone out.

Wayne of Campbell

Wayne