Sunday, July 08, 2012

Memory Leak sortedArrayUsingSelector:@selector(caseInsensitiveCompare:) and dictionaryWithContentsOfFile

I was using Instruments to check for memory leaks and I kept getting a memory leak loading an NSDictionary using:

[NSDictionary dictionaryWithContentsOfFile:finalPath];

And another memory leak on:

self.displayData = [data sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];

So, I started to wonder, does dictionaryWithContentsofFile and sortedArrayUsingSelector actually init objects that I need to release? (Answer: NO!) But I was doubting myself and the code.

In my view I load some data from a plist and transfer it to an array.

- (void)loadData {
    if (nil != self.displayData) {
        [self unloadData];
    NSArray *defaultData = nil;
    if ( ([self shouldLoadAll] == YES) || ([self shouldLoadDefault] == YES) ) {
        NSString *path = [[NSBundle mainBundle] bundlePath];
        NSString *finalPath = [path stringByAppendingPathComponent:self.propertyListFileName];
        NSDictionary *plistDictionary = [NSDictionary dictionaryWithContentsOfFile:finalPath];
        defaultData = [plistDictionary valueForKey:self.propertyListDictionaryKey];
    NSArray *customData = nil;
    if ( ([self shouldLoadAll] == YES) || ([self shouldLoadCustom] == YES) ) {
        customData = [self customItems];
    [self transferData:defaultData customData:customData];

- (void)transferData:(NSArray*)defaultData customData:(NSArray*)customData {
    NSMutableArray *data = [[NSMutableArray alloc] init ];
    [data addObjectsFromArray:defaultData];
    [data addObjectsFromArray:customData];
    self.displayData = [data sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
    [data release];

At the top of the .m file:
@synthesize displayData = _displayData;

In the .h file:
@property (nonatomic, retain) NSArray *displayData;

The above code was from a newly created "base" class where I was removing lots of duplicate code. The problem was I that I did not remove the synthesized variables from the old classes that were now subclasses of the newly created bass class.

The call:
self.displayData = [data sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];

was hitting two properties. However, the new base class had a dealloc with this code:

    [_displayData release];

That is there was only one release in the base class.

That was a pain to track down!

That is why I wanted to share this!

I didn't try to add a release in the sub class, because it was wrong to have the property synthesized twice.

If you are having memory leaks that don't make any sense, make sure you don't have a base class and subclass that have the same property like I did!