Question

Based on Bavarious's answer to this SO question, I am led to believe that @autoreleasepool is now an Objective-C language feature if you build with LLVM/clang.

This being the case, how does one rewrite the following code to use an @autoreleasepool instead of an NSAutoreleasePool in a non-ARC environment?

[NSAutoreleasePool addObject:anObject]; // (*)

Background: I essentially want to write a custom implementation of -autorelease that does not in any way interact with the NSAutoreleasePool class:

@autoreleasepool {
    SomeCls *obj = [[SomeCls alloc] init];
    [obj autorelease]; // Does not go through an NSAutoreleasePool object
    // ...
}
Was it helpful?

Solution

@autoreleasepool { } is a new language feature meant to obviate the need to drain the current pool at every exit point of a function. Now, instead of having to write:

void f(void) {
    //Make a new pool
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
    //Every inner scope that exits prematurely requires a drain
    if (life_is_good) {
        for (int i = 0; i < 1000; i++) {
            NSObject *obj = [[[NSObject alloc]init]autorelease];
            //breaks out of the loop, and the function, so drain the pool
            if (life_is_bad) {
                [pool drain];
                return;
            }
        }
        //Life gets bad below here, so return
        [pool drain];
        return;
    }
    //end of function requires drain.
    [pool drain];
}

You can wrap the entire function in an @autoreleasepool { } directive and the compiler will insert the proper drains where the function returns:

void f(void) {
    //Make a new pool
    @autoreleasepool {
        if (life_is_good) {
            for (int i = 0; i < 1000; i++) {
                NSObject *obj = [[[NSObject alloc]init]autorelease];
                //breaks out of the loop, and the function, so drain the pool
                if (life_is_bad) {
                    //[pool drain]; auto-drain
                    return;
                }
            }
            //[pool drain]; auto-drain
            return;
        }
        //[pool drain]; auto-drain
    }
}

I have no idea why you would want to forgo the automatic pool mechanism, as it handles a huge amount of work for you (page management, per-thread pool management, releasing objects in a timely yet efficient manner), but it shouldn't be that much of a hassle to get a primitive NSAutoreleasePool up and running. Any autoreleasepool would require a page manager (malloc() and realloc()), pointer storage (a simple array), and some way to detect whether or not the current scope in which it is being used has exited (-drain). A simple pool could just be a thread-safe singleton, but be wary of how many objects you try to insert and remove at one time with that implementation. You just might segfault if you try to allocate too many hot pages for too many objects.

OTHER TIPS

Based on Bavarious's answer to this SO question, I am led to believe that @autoreleasepool is now an Objective-C language feature if you build with LLVM/clang.

Correct.

This being the case, how does one rewrite the following code to use an @autoreleasepool instead of an NSAutoreleasePool in a non-ARC environment?

[NSAutoreleasePool addObject:anObject]; // (*)

Doc: Normally you don’t invoke this method directly—you send autorelease to object instead.

@autoreleasepool {
 id anObject = ...;
 [anObject autorelease];
}

Background: I essentially want to write a custom implementation of -autorelease that does not in any way interact with the NSAutoreleasePool class:

You can't even subclass NSAutoreleasePool any longer (in a meaningful way). Unless you never pass the object to other APIs which are free to autorelease, your objects are going to end up in the current autorelease pool. The mechanics of this today is not even object based.

If this is impossible, I would be open to workaround suggestions not involving NSAutoreleasePool.

Not certain what problem you are trying to solve, but you can extend lifetimes using:

NSMutableArray * fauxAutoreleasePool = NSMutableArray.new;
id anObject = ...;
[fauxAutoreleasePool addObject:anObject];
...
[fauxAutoreleasePool removeAllObjects]; // << explicitly drain, which also happens when the array is destroyed

Of course, you could write your own implementation. Why you would write your own is still a mystery, and not a good idea for production code (@autoreleasepool works well).

Though I have already marked an answer as accepted, and offered a bounty at the time, I have since found the true answer I was searching for and realised my question wasn't very clear - primarily because I didn't know what to ask.

How to interact with @autoreleasepool:

When the compiler comes across an @autoreleasepool { } block, it automatically inserts two function calls. At the opening of the block it inserts the C function call:

void *_objc_autoreleasePoolPush(void);

At the close of the @autoreleasepool block (including when a return or break is encountered within - but NOT when an exception is thrown according to the LLVM docs) the compiler inserts the C function call:

_objc_autoreleasePoolPop(void *ctxt); // ctxt is Apple's label for the param

I believe the void *ctxt parameter is an indicator of where the current @autoreleasepool started (really only useful in Apple's implementation - see here). In any case I found it can easily be ignored in custom implementations.

The -autorelease selector:

Essentially the methodology is this:

  • Create (if outermost block) or mark (if inner block) an autorelease pool object (in C or C++) in _objc_autoreleasePoolPush(). It seems easiest to me to use a Stack data structure for this but YMMV.
  • Ensure this object is in scope within the -autorelease selector, or you have a fail-safe way of accessing it.
  • Add the object to the autorelease pool object (in whatever way that may be) inside -autorelease.
  • In _objc_autoreleasePoolPop(void *), send the -release message to every item in the autorelease pool object until the marker set in _objc_autoreleasePoolPush() is reached (or you reach the bottom of the pool). Then do any additional required cleanup.

Final notes on threading:

Objective-C @autoreleasepools are supposed to be thread-local. That is, each thread has its own (if it requires one). As a result, _objc_autoreleasePoolPush() must have some way of determining whether the current thread already has an active autorelease pool and create the object if it does not.

Similarly, this is why, when creating a thread, the thread must first open an @autoreleasepool { } block before it does anything else (in Objective-C), and the final thing it must do is close that @autoreleasepool block (implicitly through break or return or with explicit fall-through).

Three final observations:

  1. If you wish to implement your own autorelease pool object, you will need some form of thread-local storage to store it.
  2. That last point about threading is why every Objective-C program must begin with an @autoreleasepool { } block in main().
  3. This is why Apple ask you to use the NSThread class and GCD rather than DIY threading - they take care of all of this for you!

At the end of the @autoreleasepool block, the autoreleased objects are sent a release message.

If you want to reimplement the autorelease method, you may not want to directly send release messages to your NSObjectsubclass. If so, reimplementing release with some special behavior could do your business.

Background: I essentially want to write a custom implementation of -autorelease that does not in any way interact with the NSAutoreleasePool class:

What makes you think that -autorelease interacts with NSAutoreleasePool?

As long as you are running on iOS 5+ or Mac OS X 10.7+, both the @autoreleasepool and -[NSObject autorelease] will use the runtime autorelease pool management functions, and have nothing to do with NSAutoreleasePool at all (and NSAutoreleasePool on those OS versions is simply a wrapper around the runtime functions).

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