96

As I understand it, anything created with an alloc, new, or copy needs to be manually released. For example:

int main(void) {
    NSString *string;
    string = [[NSString alloc] init];
    /* use the string */
    [string release];
}

My question, though, is wouldn't this be just as valid?:

int main(void) {
    NSAutoreleasePool *pool;
    pool = [[NSAutoreleasePool alloc] init];
    NSString *string;
    string = [[[NSString alloc] init] autorelease];
    /* use the string */
    [pool drain];
}
RK-
  • 12,099
  • 23
  • 89
  • 155
James Sumners
  • 14,485
  • 10
  • 59
  • 77

7 Answers7

69

Yes, your second code snippit is perfectly valid.

Every time -autorelease is sent to an object, it is added to the inner-most autorelease pool. When the pool is drained, it simply sends -release to all the objects in the pool.

Autorelease pools are simply a convenience that allows you to defer sending -release until "later". That "later" can happen in several places, but the most common in Cocoa GUI apps is at the end of the current run loop cycle.

chakrit
  • 61,017
  • 25
  • 133
  • 162
kperryua
  • 10,524
  • 1
  • 38
  • 24
  • 5
    where's the end of the current run loop cycle, if I don't have an loop? – Thanks Apr 13 '09 at 07:23
  • 24
    Shouldn't "outer-most" be "inner-most"? – Mike Weller Jun 08 '10 at 15:33
  • `an object` should be `an object that is a subclass of NSObject or NSProxy and doesn't override -autorelease`. –  Apr 23 '11 at 11:18
  • 1
    EDIT: Changed outer-most to inner-most. – chakrit Dec 29 '11 at 05:30
  • 1
    Important : If you use Automatic Reference Counting (ARC), you cannot use autorelease pools directly. Instead, you use @autoreleasepool blocks. From https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSAutoreleasePool_Class/Reference/Reference.html – Md Mahbubur Rahman Mar 28 '13 at 06:11
  • @Thanks The run loop is not a loop. It's not a language construct like `for` or `while`. It's rather a dispatch mechanism that manages the different execution states and events within a thread. –  Jul 07 '13 at 07:44
37

NSAutoreleasePool: drain vs. release

Since the function of drain and release seem to be causing confusion, it may be worth clarifying here (although this is covered in the documentation...).

Strictly speaking, from the big picture perspective drain is not equivalent to release:

In a reference-counted environment, drain does perform the same operations as release, so the two are in that sense equivalent. To emphasise, this means you do not leak a pool if you use drain rather than release.

In a garbage-collected environment, release is a no-op. Thus it has no effect. drain, on the other hand, contains a hint to the collector that it should "collect if needed". Thus in a garbage-collected environment, using drain helps the system balance collection sweeps.

mmalc
  • 8,201
  • 3
  • 39
  • 39
  • 4
    It is fundamentally impossible to 'leak' a `NSAutoreleasePool`. This is because pools operate like a stack. Instantiating a pool pushes that pool on to the top of that threads autorelease pool stack. `-release` causes that pool to pop from the stack **AND** any pools that were pushed on top of it, but for whatever reason were not popped. – johne Sep 06 '09 at 03:12
  • 7
    In what way is this relevant to what I wrote? – mmalc Jan 26 '10 at 16:19
  • 2
    I like how he took the time to bold AND. *SNAP!* – Billy Gray Nov 18 '10 at 19:42
17

As already pointed out, your second code snippet is correct.

I would like to suggest a more succinct way of using the autorelease pool that works on all environments (ref counting, GC, ARC) and also avoids the drain/release confusion:

int main(void) {
  @autoreleasepool {
    NSString *string;
    string = [[[NSString alloc] init] autorelease];
    /* use the string */
  }
}

In the example above please note the @autoreleasepool block. This is documented here.

Neovibrant
  • 747
  • 8
  • 16
7

No, you're wrong. The documentation states clearly that under non-GC, -drain is equivalent to -release, meaning the NSAutoreleasePool will not be leaked.

kperryua
  • 10,524
  • 1
  • 38
  • 24
  • I wondered why Xcode would generate code with -drain if that were the case. I used -drain because I thought it was equivalent to -release based on the code generated by Xcode. – James Sumners Sep 16 '08 at 00:01
  • 1
    It is fundamentally impossible to 'leak' a `NSAutoreleasePool`: http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmAutoreleasePools.html#//apple_ref/doc/uid/20000047-997594 – johne Sep 06 '09 at 03:18
0

What I read from Apple: "At the end of the autorelease pool block, objects that received an autorelease message within the block are sent a release message—an object receives a release message for each time it was sent an autorelease message within the block."

https://developer.apple.com/library/mac/documentation/cocoa/conceptual/MemoryMgmt/Articles/mmAutoreleasePools.html

Gagan_iOS
  • 3,638
  • 3
  • 32
  • 51
0

sending autorelease instead of release to an object extends the lifetime of that object at least until the pool itself is drained (it may be longer if the object is subsequently retained). An object can be put into the same pool several times, in which case it receives a release message for each time it was put into the pool.

Hardik Mamtora
  • 1,642
  • 17
  • 23
-2

Yes and no. You would end up releasing the string memory but "leaking" the NSAutoreleasePool object into memory by using drain instead of release if you ran this under a garbage collected (not memory managed) environment. This "leak" simply makes the instance of NSAutoreleasePool "unreachable" like any other object with no strong pointers under GC, and the object would be cleaned up the next time GC runs, which could very well be directly after the call to -drain:

drain

In a garbage collected environment, triggers garbage collection if memory allocated since last collection is greater than the current threshold; otherwise behaves as release. ... In a garbage-collected environment, this method ultimately calls objc_collect_if_needed.

Otherwise, it's similar to how -release behaves under non-GC, yes. As others have stated, -release is a no-op under GC, so the only way to make sure the pool functions properly under GC is through -drain, and -drain under non-GC works exactly like -release under non-GC, and arguably communicates its functionality more clearly as well.

I should point out that your statement "anything called with new, alloc or init" should not include "init" (but should include "copy"), because "init" doesn't allocate memory, it only sets up the object (constructor fashion). If you received an alloc'd object and your function only called init as such, you would not release it:

- (void)func:(NSObject*)allocd_but_not_init
{
    [allocd_but_not_init init];
}

That does not consume any more memory than it you already started with (assuming init doesn't instantiate objects, but you're not responsible for those anyway).

matthias
  • 2,419
  • 1
  • 18
  • 27
Loren Segal
  • 3,251
  • 1
  • 28
  • 29
  • I don't feel comfortable leaving this answer as accepted when your information about drain isn't quite right. See http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSAutoreleasePool_Class/Reference/Reference.html#//apple_ref/doc/uid/20000051-SW5 Update and I will re-accept. – James Sumners Sep 16 '08 at 04:16
  • What is inaccurate about the reply? In a garbage collected environment (as stated), drain does not delete the AutoReleasePool, so you *will* leak memory unless you used release. The quote I listed was straight from the horse's mouth, the docs on drain. – Loren Segal Sep 17 '08 at 19:27
  • 1
    Loren: Under GC, -[NSAutoreleasePool drain] will trigger a collection. -retain, -release, and -autorelease are all ignored by the collector; that's why -drain is used on autorelease pools under GC. – Chris Hanson Sep 23 '08 at 08:04
  • In the documentation for 'drain': In a managed memory environment, this behaves the same as calling release. Thus you will *not* leak memory if you use 'drain' instead of release. – mmalc Oct 08 '08 at 01:17
  • `-[NSAutoreleasePool release]` in a garbage-collected environment is a no-op. `-[NSAutoreleasePool drain]` works in both reference-counted and garbage-collected environments. – Jonathan Sterling Dec 08 '09 at 01:02