1

I've read a few posts on this, but there's still one thing that's not clear for me. I know this might be rather a n00b question, but I've actually got rather far into development without quite grasping this fundamental issue. A symptom of being self taught I guess.

You declare a variable in your header, like so:

@interface SomeClass : NSObject {
    NSMutableArray *anArray;
}

@property (nonatomic, retain) NSMutableArray *anArray;

end

And then in your main file you synthesise it and set it to an initial value:

    @implementation SomeClass

@synthesize anArray

- (SomeClass *)init{
    if (self = [super init]) {
        self.anArray = [[NSMutableArray alloc] initWithCapacity:10];
}
[return self];

And release it when your Class deallocs:

- (void)dealloc {
[anArray release];
[super dealloc];
}

Now, when I run instruments, the line

self.anArray = [[NSMutableArray alloc] initWithCapacity:10];

is identified as a memory leak. Is it a memory leak because when you define the variable anArray in the header it allocates memory? (Because I thought it was a null pointer.) Therefore when you want to initialise it, and you call [[NSMutableArray alloc] initWithCapacity:10], you are reallocating the memory, and losing the pointer to the original allocation?

So instead, I use the convenience class method:

@implementation SomeClass

    @synthesize anArray

    - (SomeClass *)init{
        if (self = [super init]) {
            self.anArray = [NSMutableArray arrayWithCapacity:10];
    }
    [return self];

This is no longer identified as a memory leak in instruments. And since it's a convenience method, anArray is autoreleased. However, if I am to assume that the instance declaration in the header allocates memory, which would explain the previous issue, then should I still release anArray? Does setting the initial values in this way retain it perhaps?

I understand the difference between

NSMutableArray *anArray = [[NSMutableArray alloc] initWithCapacity:10];

and

NSMutableArray *anArray = [NSMutableArray arrayWithCapactiy:10];

but what I'm not sure I understand is when you've declared NSMutableArray *anArray in your header, which of the two approaches you should use and why. And whether or not if you use the second approach, you should still release anArray when you call dealloc.

I might add that I've found the following posts/links useful:

Community
  • 1
  • 1
Smikey
  • 8,106
  • 3
  • 46
  • 74

2 Answers2

3

alloc'ing an object starts it off with a reference count of 1. Setting a property that has the 'retain' attribute also increases the reference count.

So, that means this is usually bad:

@property (nonatomic, retain) Object * variable;

...

self.variable = [[Object alloc] init];

Because variable now has a reference count of 2.

When setting a object's member variable, just do this:

variable = [[Object alloc] init];

You should also realize that this works

        self.anArray = [NSMutableArray arrayWithCapacity:10];

Because "arrayWithCapacity" (and other similar factor methods) autoreleases the object it returns, so after you set the property, it essentially has a reference count of 1.

whooops
  • 935
  • 6
  • 11
  • Thanks for making it so clear. So if I do self.anArray = [NSMutableArray arrayWithCapacity:10]; I STILL need to release anArray when I dealloc, since it still has a reference count of 1. Does the reference count go up by 1 every time I call self.anArray = something then? In which case it would be safer to set the ivar directly, without using self? – Smikey Oct 06 '10 at 17:09
  • 1
    Yes, you would still need to release it. The number doesn't "go up" every time you call 'self.anArray = something', because the reference count is tied to the object, not the variable. Normally, 'self.anArray = something' will release the old object and retain the new one. – whooops Oct 07 '10 at 02:57
  • 1
    It's generally good practice to use 'self.varname = something', because that way you will always release the old object. The exception, as I showed, is in the init method, and you've just alloc'd the object, and there's definitely no previous object stored in the variable. – whooops Oct 07 '10 at 02:59
2

It's not the instance that allocates the memory. You're right to assume that in Objective-C (at least on all Apple-based operating systems), newly initialized classes have all their ivars set to 0 (or nil or NULL as appropriate).

The problem you're seeing is that you're using the property, not the ivar in your initialization. Since you declared your property as retain, using the property accessor to set it automatically retains it.

So, when you initialize you either have to take ownership and set the ivar directly, or do like you're doing and use the property accessor to set the property and then relinquish ownership in the init method (by either releasing an object you own or, as you did in your second instance, using the convenience constructor so that you never owned the returned instance).

So just remember, if you ever use the property accessors, even within the class itself, you will get the features you set on the property (e.g., nonatomic, retain, etc.). You use the property accessors whenever you do one of the following:

// in these cases the property takes ownership through the
// retain keyword, so you must not take ownership yourself
self.anArray = something;
[self setAnArray:something];
[self setValue:something forKey:@"anArray"];

You would access your ivar directly like:

anArray = something; // in this case you must take ownership
Jason Coco
  • 77,985
  • 20
  • 184
  • 180
  • Thanks for the detailed answer - it really helps. The one bit I don't get it when you say "use the property accessor to set the property and then relinquish ownership in the init method (by either releasing an object you own..." So if I call self.anArray = [[NSMutableArray alloc] init] in the init method, I need to release it in the init method too? And also when I dealloc? So that's why people sometimes do NSArray *anArray = [[NSArray alloc] init]; self.classArray = anArray; [anArray release]; ? – Smikey Oct 06 '10 at 17:28
  • @Smikey: Yes, that's exactly why they do that. So if you do the original (i.e., self.anArray = [[NSMutableArray alloc] init]) you are basically guaranteed to leak it. Basically, since the property is declared as "retain", it takes ownership, so you need to relinquish ownership (i.e., release it). – Jason Coco Oct 06 '10 at 17:43
  • So my options are: anArray = [[NSArray alloc] init] (must call [anArray release] in dealloc only || self.anArray = [NSArray array] (no need to release anything) || if I DID do self.anArray = [[NSArray alloc] init] (then must call [anArray release] in init method ASWELL as in dealloc method (BAD)) || finally, the method I mentioned in my comment. And which is better out of anArray = [[NSArray alloc] init] and self.anArray = tempArray (where tempArray is alloced and released as above). Thanks so much btw! – Smikey Oct 06 '10 at 17:58
  • Any time you have a copy or retain attribute, you must release it in dealloc, so that doesn't change. You're still responsible for cleaning that up. The only difference is how you /assign/ it in the first place. Inside your class, you can assign it directly to the ivar, in which case you must keep and maintain ownership (i.e., create and not release it or retain it). If you assign it through property accessors, you must release ownership (e.g., if you create it, you must release it). – Jason Coco Oct 06 '10 at 18:06
  • Ooooh k... I think I see. So it's better not to use self.ivar to assign values in the init method for a class, since you'll then need to release the ivar. However, if you assign values directly to the ivar, then the releasing and retaining depends on how you assign it - either with a class method (autoreleases) or with an instance method (you must still release it). Is that right? Thanks for your patience btw :s – Smikey Oct 06 '10 at 18:25
  • When you say you must keep and maintain ownership, doesn't that mean you're responsible for releasing and retaining it? Whereas if you don't keep or maintain it, you don't have to release/retain it. I think that's where I'm getting confused with your last comment :o – Smikey Oct 06 '10 at 20:08