views:

73

answers:

2

I came across a problem that seems to be called "drowning" in autorelease pools.

My code creates and destroys objects correctly. However, I use some class methods that autorelease several variables without my knowing about it. Considering they loop thousands and thousands of times every minute... I find myself drowning in thousands of unreleased objects lost somewhere in the program.

2 questions:

  1. So either I do not use class methods AT ALL - is that a permanent solution, or, even so, will some instance methods (Foundation, UIKit) still autorelease objects without my knowing about it?

  2. or I can alloc an autorelease pool prior to calling class methods and draining it after calling the class methods - would this be a permanent solution?

+2  A: 
  1. Just because you don't use a class method doesn't mean you're going to avoid autoreleased objects. For example: NSString * path = [anotherString stringByAppendingPathComponent:@"foo"] returns an autoreleased object, yet no class methods were involved. As far as I know, the only way to avoid autoreleased objects is to not use objects. If you really want to go that route, check out CoreFoundation. (I do not recommend going that route.)

  2. If you do have some crazy factory method that creates tons of autorelease pools, then I'd probably do something like this:

    + (id) crazyFactoryMethodWithFoo:(id)foo {
      NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
      id returnValue = nil;
      //generate gobs of autoreleased objects
      returnValue = [something retain];
      [pool release];
      return [returnValue autorelease];
    }

    By explicitly retaining the return value before draining the pool, you ensure that the return value will live beyond the destruction of the autorelease pool. You then balance that retain by autoreleasing the return value and returning it.

Dave DeLong
@Dave DeLong that was good. The problem is I use NSUserDefaults all the time... and the methods of this class create autoreleased objects. Now I understand why Apple doesn't recommend it. I also use NSString methods all the time, like the one you mentioned. And while looping, I get these weird problems. What is your suggestion to replace NSUserDefaults in an easy way? Thanks.
GSchv
@GSchv why do you think you need to replace `NSUserDefaults`? If it returns you an autoreleased object and you want to hang on to that object beyond the lifetime of the current autorelease pool, then just `retain` the object (and then `release` it when you're done with it).
Dave DeLong
`[pool drain]` is the preferred method http://developer.apple.com/mac/library/documentation/cocoa/reference/foundation/Classes/NSAutoreleasePool_Class/Reference/Reference.html#//apple_ref/occ/instm/NSAutoreleasePool/release
cobbal
@Dave DeLong actually what I need is exactly the opposite: I want to get rid of the autoreleased objects. You see, I use NSUserDefaults all the time, like crazy, so there's lots of autoreleased objects that are not released. Do you understand my problem? See below answer, dreamlax got my point. Try looping NSUserDefaults standardUserDefaults 10000 times and it'll crash because of the pile of unautoreleased objects it returns.
GSchv
@GSchv if you're storing *that* many objects inside `NSUserDefaults`, you probably shouldn't be using it. It's intention is to store your "defaults" (ie, your preferences), and should probably only be used for small amounts of actual application data. Consider a database or CoreData instead.
Dave DeLong
@Dave DeLong Thanks. Well I solved my "drowning" problem. You just have to make sure that Foundation or UIKit do not retain the objects you ask of them - this is SO important in a game. For a repeating timer, ALWAYS invalidate. For NSUserDefaults, ALWAYS alloc and release. When adding views, ALWAYS remove from superview. It MAKES a difference when you loop a lot. Thanks for everything
GSchv
+2  A: 

Dave DeLong has already answered your questions, but usually the only times when you fill the pool too high is when you're in a very tight loop. In this case, just create another autorelease pool for each iteration of the loop.

for (NSUInteger i = 0; i < 1000000000; i++)
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    // do the work

    [pool release];
}

Objects are automatically added to the most recently created autorelease pool for the current thread. By creating one for each iteration of the loop, you can get rid of the unwanted objects quickly rather than letting them pile up in the run loop's pool.

dreamlax
@dreamlax yep that seems to be the only alternative. thanks.
GSchv