简体   繁体   中英

Objective-C (iPhone) ivars and memory management

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.

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 has 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.

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.

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM