Jon Wei
Jon Wei

Reputation: 1481

Retaining an NSArray argument - why?

I tried looking but couldn't find any help.

- (NSArray *)sushiTypes {
    return _sushiTypes;
}

- (void)setSushiTypes:(NSArray *)sushiTypes {
    [sushiTypes retain];
    [_sushiTypes release];
    _sushiTypes = sushiTypes;
}

What I would like to know is, while I was working this tutorial, I can't figure out why he is retaining the argument. I tried commenting out that retain statement, and the program works the same, no leaks. So is that just unnecessary?

I'd also like to add the fact that, I called a method like this

self.sushiTypes = [[NSArray alloc]initWithObjects:"objects...];

But when I debug it, instead of going to the -(NSArray *)sushiTypes method, it goes to the setSushiTypes method. Why is this?

Upvotes: 1

Views: 139

Answers (4)

Dan Rosenstark
Dan Rosenstark

Reputation: 69757

The reason the author puts a retain there is because this is standard setter syntax for a retain property. In ObjC, the object has to be retained unless we're sure that some other object will be responsible for keeping it alive (by retaining it).

When you remove the retain from the setter, the thing still works, because your call to the setter is wrong. It should read:

 self.sushiTypes = [[[NSArray alloc] initWithObjects:one, two, three, nil] autorelease];

because the object comes back with a retain count of 1. Google NARC and objective-c, which might bring you here. The method that calls the setter has to release (or autorelease, better) because it will soon lose the ability to do so and you'll have a leak.

Even with the correction, your setter method would work without retain, because the autorelease happens later.

This setter is the method that gets called when you call the set from the self or from another object using either the explicit syntax:

 [self setSushiTypes:whatever];

or the implicit syntax:

 self.sushiTypes = whatever;

Note: the setter gets called when you use self.sushiTypes = even if no @property is declared.

Anyway, it's clear that you're not using ARC, but with ARC most of these problems disappear. However, you should probably go through this exercise anyway, because ARC does not resolve all these types of problems.

Upvotes: 2

Inder Kumar Rathore
Inder Kumar Rathore

Reputation: 39988

It's a setter, and I'll give you the example. Suppose your _sushiTypes ivar point to objA and now you want to set your ivar to a new object say objB i.e passes as argument as sushiTypes.

1.Then in your setter you retain the objB as you are setting it to ivar and want to take the ownership. so you write [sushiTypes retain];

2.Then you releases the old object(because you are about to set new object i.e. objB to ivar) that you have retained using the same setter. So you released the old object [_sushiTypes release]; and then you assigned the retained object to the _sushiTypes ivar.

Upvotes: 0

Balakrishnan Mca
Balakrishnan Mca

Reputation: 747

Yes, more specifically, likely, it initializes the retain count to 1.

Is it the same as having [sushiTypes retain]. I know if we call retain, the object will be held until we released it.

Not the same. That will increase the retain count twice. There's not much reason to increase the retain count twice in the same place. If you did do that, you'd be responsible for calling release twice.

you have a retained sushiTypes. This sushiTypes was passed to a function as an argument. Later in the function you released it. Will it release the memory of the sushiTypes? if it is, does the sushiTypes no longer existed.

First, you should make sure that you understand that whether release frees memory or not depends on if there are other owners that are holding a reference to the object. If more than one thing has [_sushiTypes release] retained the object, then release will not free the memory in that case.

Second, bad idea. To keep sanity with reference counting, you should follow certain patterns.

retain (or alloc) instance variables, release them in dealloc or before. retain (or alloc) local variables if needed, and release them before you exit the method. The caller does not own the return value of a function. That implies that you handle a function return value like a local variable, but autorelease it before you return it instead of releasing it. That ensures it will last at least long enough for the caller to retain it, if needed. One of the patterns is not for a method caller to own a reference to a method argument, but when the function returns the caller does not own the reference. The method should not release the caller's reference.

Follow on with 2.1, instead of releasing sushiTypes. I created a local variable NSArray *sushiTypes = _sushiTypes. If I released sushiTypes will it also release sushiTypes. Or if I retain sushiTypes, will it retain _sushiTypes as well.

retain and release are sent to objects, not references. sushiTypes and _sushiTypes refer to the same object, so it's the same calling retain or release on one as on the other.

It is legitimate, but probably unnecessary, to [sushiTypes retain] and later in the same method [_sushiTypes release]. But do not do [sushiTypes release] only, thereby expropriating _sushiTypes's ownership of the object.

Upvotes: 0

nduplessis
nduplessis

Reputation: 12436

You will be pointing _sushiTypes to the value of sushiTypes. Since _sushiTypes is an instance variable, you need to take ownership of this object by retaining it. If you fail to do so and all other owners of this object releases it, this memory will be freed, leaving your instance variable to point to garbage and your app will crash.

It is important to note the order of messages in the setter as well. You need to call

[sushiTypes retain];

first, since _sushiTypes and sushiTypes might be pointing to the same object. If you swop them around and call release first, the memory might be freed before you get a chance to claim ownership.

Upvotes: 4

Related Questions