views:

80

answers:

4

I am subclassing NSURLConnection, and used MGTwitterEngine as a base to help me get started. That may be irrelevant. However, I noticed in their code they don't use @property or @synthesize for their ivars. They have wrapped the ivars in accessor methods which look like this:

- (NSString *)identifier {
  return [[_identifier retain] autorelease];
}

My question is two part. First, what effect does retain followed by autorelease have? It seems to me it would cancel itself, or worse yet leak.

Second, if I were to change the header file to have:

@property (nonatomic, retain, readonly) NSString* _identifier;

And used @synthesize indentifier = _identifier, wouldn't this do the same thing as the accessor method without having to write it?

Maybe it is just two different ways to do the same thing. But I wanted to ensure I have the correct understanding. Thanks.

A: 

Generally one retains then autoreleases if you are returning something that you didn't own the first place. It will only leak if you own _identifier and there is a mismatch of retain/alloc/etc versus release/autorelease sent to that object.

Secondly, yeah you generally don't have to write the headers if you aren't doing something special beyond what the general boilerplate looks like. Apple has good documentation on properties, I suggest if you're still fuzzy, you look at those.

jer
Even if you do "own" the something you are returning, it's still good practice to retain/autorelease so you guarantee that object is valid for the duration of the callers scope. The object returning the property may not necessarily be around that long.
Firoze Lafeer
+3  A: 

Retain followed by autorelease does exactly what you might think it does. It sends the object a retain message and then sends it an autorelease message. Remember that autoreleasing an object adds that object to the autorelease pool but does not release it yet. The autorelease pool will send the object a release message at the end of the current iteration of the run loop. So, a retain followed by autorelease essentially says, "Make sure this object stays around until the end of the the current iteration of the run loop." If you need the returned value to hang around longer, you can retain it. If not, do nothing and the autorelease pool will handle it.

In this case, the string being sent retain and autorelease messages is a property. It's already retained by the parent object. So you might wonder why do this retain and autorelease thing at all? Well, there's no guarantee that the object won't release _identifier before the current iteration of the run loop ends. Consider this example:

- (NSString *)identifier { return _identifier; }

- (void)aMethod {
    NSString *localId = [self identifier]; // localId refers to _identifier which is only retained by self
    [self methodThatChangesIdentifierAndReleasesItsOldValue];  // self releases _identifier
    NSLog(@"%@", localId); // crash, because localId (old value of _identifier) has been released
}

In this case, the returned identifier isn't retained and autoreleased. The NSLog line crashes because localId refers to a released string. However, had we used retain and autorelease in the getter, there would be no crash because localId would not be released until the end of the current iteration of the run loop.

As far as I know, your replacement with an identifier property would be just as good. It might not be identical code, but the effect should be the same.

James Huddleston
+1  A: 

Apple explains this well in Implementing Accessor Methods. The method quoted by you (named "Technique 1" by Apple) helps avoid bugs if the caller assigns a new value to the identifier property and then expects the retrieved value to still be available. It won't be needed most of the time but just returning the ivar value can lead to bugs that are hard to track down.

Ole Begemann
+1  A: 

Using @synthesize will actually only create a setter and a getter method. The code that is auto generated for you is guaranteed to use proper memory management, so that you do not need to worry.

MGTwitterEngines use of return [[ivar retain] autorelease] is actually the correct way to do it. Lets have two examples.

Assume a getter is defined as this:

-(Foo)foo {
    return foo;
}

And then we execute this code:

  1. bar = [[bar alloc] init]; // bar has aretain count of 1.
  2. foo = bar.foo; // foo har a retain count of 1 (owned by bar).
  3. [bar release]; // Bar and all it's ivars are released imidiatetly!
  4. [foo doSomething]; // This will crash since the previous line released foo.

If we instead change the getter to this:

-(Foo)foo {
    return [[foo retain] autorelease];
}
  1. bar = [[bar alloc] init]; // bar has a retain count of 1
  2. foo = bar.foo; // foo har a retain count of 2 (one owned by bar, 1 owned by autorelease pool).
  3. [bar release]; // Bar and all it's ivars are released imidiatetly!
  4. [foo doSomething]; // Will not crash since foo is still alive and owned by autorelease pool.

Hope this explains why you should always return properly autoreleased objects from all your getters. It is important that any return value can survive the deallocation of it's parent, since no class ca guarantee what a client will do with it's values once it is exposed to the wild.

PeyloW
This makes sense. Thank you for the detailed explanation. One side question, if one of my ivars were a reference to a `delegate`, I would not want to do this in favor of using `@property` with an `assign` attribute. That is to say, I should not be calling `retain`/`autorelease` on a reference to a class object, right? Only the ivars my class owns and allows accessors to.
Jason McCreary
@Jason: Right, delegates should not be memory managed, so for them simply do `return delegate`. And if the property is `copy` then do the `ivar = [arg copy]` when in the mutator method, and `return [[ivar retain] autorelease]` in the getter method.
PeyloW