Question

Still a little confused about Objective-C memory management. I think my confusion stems from what exactly the autorelease means.

NSString *theBackendResponse = [[NSString alloc] initWithData:receivedData encoding:NSASCIIStringEncoding];
NSDictionary *accountDictionary = [theBackendResponse propertyList];
[viewController setAccountDictionary:accountDictionary];

Now, what should I do with the accountDictionary in the setAccountDictionary method of my view controller? Right now I just set the instance variable "accountDictionary" to whatever is returned. Should I set it to a retained one, and then release the one that's returned? What should my setter code block look like, given that NSString's propertyList method is autoreleased?

By the way, if I release theBackendResponse, will I lose the accountDictionary? I assume not...

Was it helpful?

Solution

Calling [objectInstance autorelease] adds an object to the current NSAutoreleasePool. When that pool receives a drain message, it sends a release to all the objects in the pool. If any of those objects' retainCount reaches 0, they are deallocated at that point. The purpose of autorelease is to allow you to mark an object to be released "some time in the future". This is especially useful for things like methods that return a newly allocated object but want to release it so that the caller doesn't have to take ownership of the returned object. A method might look like this:

- (id)myMethod {
    id myObj = [[SomeClass alloc] init];

    ...

    return [myObj autorelease];
}

The caller of myMethod would then retain the return value if they wanted to take ownership of the returned value or ignore it if not. When the current NSAutoreleasePool is drained, myObj will get a release message. If no other objects own it (i.e. have sent it a retain message), it will get deallocated.

All of this is explained in the Cocoa Memory Management Programming Guide. Even if you've already read it, it's always worth an other read.

So, to answer your questions:

First, you should release theBackendResponse. You will leak memory if you do not. You don't need to know what accountDictionary does with the string: if it needs to keep a reference it will have retained theBackendResponse. You have an ownership of theBackendResponse because you alloc'd it, so you must relinquish that ownership (via release or indirectly via autorelease).

Second, you must retain or copy the argument to setAccountDictionary: if you want to keep a reference to that object or value respectively. The standard setter method looks something like this (assuming you do not need atomic semantics):

-(void)setAccountDictionary:(NSDictionary*)newDict {
  if(newDict != accountDictionary) {
    id tmp = accountDictionary;
    accountDictionary = [newDict copy]; //Since newDict may be mutable, we make a copy so that accountDictionary isn't mutated behind our back.
    [tmp release];
  }
}

You must also remember to release accountDictionary in the dealloc method:

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

Since you appear to be using NSViewController, I assume you're on Leopard (OS X 10.5) in which case, you should probably be using @property and the @synthesized getter/setter if possible. To do this, add a

@property (copy,readwrite) NSDictionary * accountDictionary; 

declaration to the class @interface. And add a @synthesize accountDictionary; directive in the @implementation block for your controller class.

OTHER TIPS

In general, one object or method should not have to care about how another is managing memory. The fact that somebody else has autoreleased something is irrelevant to you. It's simpler to think of the concept of ownership. So retain and some other methods claim ownership, and release and autorelease relinquish it. If an object needs to keep a reference to another, it should claim ownership for as long as it needs. Thus, setter methods usually either retain or copy the new value and release or autorelease the old value.

I strongly recommend reading the Cocoa memory management guidelines. They're not all that long or complicated, and it's very important to understand them.

The set accessor method should always copy / retain the incoming value before releasing the old, in the case where the old value is the only object that owns the new value:

-(void)setAccountDictionary:(NSDictionary*)newDict {
    id old = accountDictionary;
    accountDictionary = [newDict copy];
    [old release];
}

If accountDictionary referred to newDict and the retain count for newDict was 1, the call to [accountDictionary release] before the call to [newDict copy] would cause the retain count to got to 0 and therefore release newDict.

As an example of incorrect code, where we release the old dictionary and then copy the new dictionary:

-(void)setAccountDictionary:(NSDictionary*)newDict {
    [accountDictionary release];
    accountDictionary = [newDict copy];
}

and have the following code:

NSDictionary *dict = [obj accountDictionary];
[obj setAccountDictionary:dict];

It's contrived, but it demonstrates that in the setter, accountDictionary and newDict refer to the same instance. If the retain count is 1, the [accountDictionary release] line will decrease the retain count to 0, and thus release the instance from memory. [newDict copy] will now refer to an invalid instance.

Apple describes several concepts when implementing accessors: Memory Management Accessor Methods
If you can use Objective-C 2.0, I would go with properties and dot syntax. Properties are new in Objective-C 2.0 and provide auto accessor generation.
In the .h File:

@property (retain) NSDictionary* accountDictionary;

In the implementation:

@synthesize accountDictionary;

Synthesize generates accessor methods for your NSDictionary. (If you want to provide your own implementation, you could also do that)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top