Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 my 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

like image 566
Shabbyrobe Avatar asked Dec 04 '22 13:12

Shabbyrobe


2 Answers

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.

like image 166
Darren Avatar answered May 07 '23 19:05

Darren


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.

like image 44
Hua-Ying Avatar answered May 07 '23 18:05

Hua-Ying