2

I'm doing my last bit of memory management tidying and there's something I don't understand. I've checked all the documentation, Stack Overflow, etc. but still don't get it. I suspect it's to do with arrays.

I have an NSMutableArray as an instance variable which I use to hold objects created from objects in another array.

-viewDidLoad initialises the array as follows:

self.photoAlbum = [[NSMutableArray alloc] initWithCapacity:100];

It then calls a method which populates them.

int i = 0;
for (Gem *gem in self.entityArray) {
    NSString * filePath = [[NSString alloc] initWithFormat: @"%@/%@2.jpg", [sysPaths objectAtIndex: 0], gem.detailsTitle];  
    // there is some stuff in here that means that there isn't a one to one relationship between the objects in gem and those in photo
    Photo *photo = [[Photo alloc] init];
    photo.filePath = filePath;
    photo.title = gem.title;
    photo.index = [NSNumber numberWithInt:i];
    [self.photoAlbum addObject:photo];
    [filePath release];
    [photo release];
    i++;
}

In Instruments, it shows I'm leaking Photo objects and I'm not sure why.

The photoAlbum property is:

@property (nonatomic, retain) NSMutableArray *photoAlbum;

What am I missing?

2 Answers 2

7

The problem is that the setter of your property has retain semantics. You need to autorelease when you are setting the property, like so:

self.photoAlbum = [[[NSMutableArray alloc] initWithCapacity:100] autorelease];

or, even better:

self.photoAlbum = [NSMutableArray arrayWithCapacity:100];

The reason this is the case is that the setter you've generated by synthesizing that property looks something like this (simplified):

- (void)setPhotoAlbum:(NSMutableArray *)array {
  [photoAlbum autorelease];
  photoAlbum = [array retain];
}

So, what is happening is:

[photoAlbum autorelease];
photoAlbum = [[[NSMutableArray alloc] initWithCapacity:100] retain]; // 0 + 2 = 2

// in -dealloc:
[photoAlbum release]; // 2 - 1 = 1

Hence, photoAlbum is never released enough times to be deallocated, since -release looks something like this (heavily simplified):

- (void)release {
  retainCount = retainCount - 1;
  if (retainCount == 0) {
    [self dealloc];
  }
}

(I want to reiterate that this is basically what the implementation is doing, but not what it looks like in real life). The point is that you'll not trigger a deallocation until you've balanced your releases against your retains.

Do not take this as an exhortation to EVER look at -retainCount, ever, ever. Other objects existing at runtime may be retaining your object and doing things with it that you don't know about; therefore, the actual retain count of your object at any given time is useless to you. Please never, ever use it when managing memory. @bbum will thank you.

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks Jonathan. Is this peculiar to arrays? Is my @property statement the most appropriate?
Lol, I thought about posting an answer where I just suggested they use while ([photoAlbum retainCount] > 0) [photoAlbum release] for April Fools, but I figured that'd be too cruel for you. :)
@Chris That's how property declaration will behave with all Objective-C objects. nonatomic,retain is definitely appropriate for mutable arrays, in answer to your other question.
@Jonathan Sterling: If you had done the while ([photoAlbum retainCount] > 0) [photoAlbum release] thing I would have to have down voted you since the idea is not original (check out the first comment to my answer here) stackoverflow.com/questions/4042934/calling-dealloc-in-init/…
@Jonathan: By the way, your hypothetical code for -release is wrong. The check for necessary deallocation is done before decrementing retainCount.
-1

You are probably missing

[photoAlbum release];

in the dealloc method.

Edit

Actually, I was wrong. The problematic line is

self.photoAlbum = [[NSMutableArray alloc] initWithCapacity:100];

The array you create is one you own, but you don't release it. This will fix it:

self.photoAlbum = [[[NSMutableArray alloc] initWithCapacity:100] autorelease];

3 Comments

Yeah, the problem is not -dealloc, but that he's setting a retained object, which gets retained again in the setter.
Make it even easier: self.photoAlbum = [NSMutableArray array]; There is very little to be gained with "initWithCapacity". Less code it generally better.
@Jonathan: I noticed the issue with the assignment pretty much as soon as I'd posted and added the correction in less than 30 seconds but somebody still managed to get in and down vote it first.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.