How do you manage releasing an object created in an instance method on iPhone without autorelease?
I'm fairly new to iPhone development and I've hit a roadblock in my understanding of memory management. I've read through the Memory Management Guide for Cocoa and have read many, many questions and answers on SO, but have not found a complete answer.
If I have an instance method that creates an object, every example I've seen seems to use an autorelease call:
-(NSArray *)findThings {
NSArray* things = [[[NSArray alloc] init] autorelease];
// add some lovely things to m开发者_如何转开发y shiny new array
return things;
}
Forgetting the contrived example, everything I've read about iPhone dev best practices says that autorelease pools are discouraged, but how can I implement the above example without one? If this method is called many, many times, it feels like I run the risk of clogging the iPhone's autorelease pool with, well, "things", which seems to go against the need to keep resource use to a minimum on such a constrained platform.
I considered the following:
-(NSArray *)findThings {
NSArray* things = [[NSArray alloc] init];
// add some lovely things to my shiny new array
[things release];
return things;
}
But then 'things' would have a retain count of zero before it passes to the calling method, so I feel like there's a significant risk of the object being deallocated between the call to [things release] and the method which calls findThings actually using the result.
I was a bit confused by the rule from the Memory Management Guide which states:
A received object is normally guaranteed to remain valid within the method it was received in. (...) That method may also safely return the object to its invoker.
I wasn't sure whether this meant as the writer of an instance method I could safely perform the release without the risk of the object being deallocated until the calling method's scope ended, or whether as a user of classes in the frameworks provided by apple I could assume that I didn't have to worry about retain/release/etc for objects I received from those classes as long as the method didn't have new/init/alloc/copy/whatever in its name.
So to summarise,
- Can I use release before returning an object instead of autorelease to avoid using an autorelease pool on the iPhone?
- If not, is there a better pattern for this that doesn't involve the autorelease pool?
- Am I missing something fundamental here? It seems like there's a hole in the docs.
Thanks in advance
Forgetting the contrived example, everything I've read about iPhone dev best practices says that autorelease pools are discouraged
Autorelease pools are not discouraged; they're used everywhere in Cocoa, and when used correctly they have zero impact on memory usage. Autorelease pools are only a concern when an they artificially extend the life of a large number of objects, say in a loop, in which case you would manage your autorelease pools manually.
Yes, you're right autorelease pools are discouraged for iPhone development when they're not absolutely necessary. They are discouraged because it allows objects to stay in memory longer then necessary this is a problem when developing applications for the iPhone, where memory is scarce. Autoreleased objects don't get released until it makes it back to the main autorelease pool created in main.c and this could be a long time. (unless you created your own autorelease pool, I'll talk about this last.)
A case where an autoreleased object is not necessary:
NSString *string = [NSString stringWithString:@"hello world"];
//use string
The first case is more convenient since I don't have to worry about releasing string
later but it could have been easily been replaced with:
NSString *string = [[NSString alloc] initWithString:@"hello world"];
// use string
[string release];
For these cases avoiding the autoreleased object is recommended.
When you're passing back objects using autorelease is unavoidable.
You must use:
-(NSArray *)findThings {
NSArray* things = [[[NSArray alloc] init] autorelease];
// add some lovely things to my shiny new array
return things;
}
Since the object is invalid as soon as you call release
.
-(NSArray *)findThings {
NSArray* things = [[NSArray alloc] init];
// add some lovely things to my shiny new array
[things release];
// things is now in invalid object if you could potentially use things later it should be set to nil to avoid a crash
things = nil; // sending messages to nil is okay, it won't cause a crash, sending messages to a released object is not okay, it can cause unpredictable crashes.
return things;
}
In cases where you're using a large amount of autoreleased memory you should create and drain your own autorelease pool so the autorelease memory doesn't stay around longer then necessary.
-(void)useLotsOfAutoReleasedObjects {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// lots of autoreleased object
// large autoreleased objects
[pool drain]; // autorelease objects are released too
}
[pool drain] also releases the pool, see the NSAutoreleasePool Reference for more information.
Well autorelease
is there to cover the exact scenarios that you are having problem with.
You follow the Create Rule.
save yourself the hassle.. just do:
- (NSArray *)findThings{
NSArray *things = [NSArray array];
return things;
}
as stefanB mentioned earlier, autorelease is here to cover the problem you are having here. another solution could be that you just retain it, and release it where you use it.. but this could cause leaking if you are not carefull.
As everyone else is saying, this problem is what autorelease was designed for.
When creating and returning objects from an instance method, the object should be auto released before being returned.
This forces the invoker to follow correct memory management rules without going the extra mile to ensure the fact.
When a sender is returned an object from a method that doesn't contain the words Create, Alloc, Retain or New, then the result is expected to be autoreleased by convention. This lets the programmer know, easily, that the object should be retained if they want to keep it around, or that its safe to use and forget about within the current scope, without having to worry about its memory.
精彩评论