views:

215

answers:

2

I have a Cocoa plug-in that is loaded into an existing Carbon application.

When the plug-in is first loaded, the Carbon application calls an initialization function, Plugin_Init() and in that function I set up the environment like so:

//this is the global autorelease pool
static NSAutoreleasePool* globalPool = nil;

void Plugin_Init()
{
    NSApplicationLoad(); //loads the Cocoa event loop etc
    //create an autorelease pool
    globalPool=[[NSAutoreleasePool alloc] init];

    //callback functions are registered here
    Plugin_defineFunction("doSomething",doSomething,0);
}

However, the Carbon app does not send any notifications when the application is about to terminate.

Is it actually necessary to clean up the "global" autorelease pool that I've created when the app terminates?

I tried registering for the Carbon app quit event by adding a call to the registerForApplicationQuitNotification() function below, but when the application terminated I received warnings that I was calling -release on an invalid autorelease pool. Is there a problem with how I'm handling the Carbon events?

//handles the Carbon application quit notification
static pascal OSStatus handleApplicationQuitEvent(EventHandlerCallRef nextHandler, EventRef evt, void *ud)
{
    OSStatus err = noErr;
    UInt32 evtkind;
    evtkind = GetEventKind( evt );
    if ( evtkind == kEventAppQuit ) 
    {
        //release the global autorelease pool
        [globalPool release];
    }
    // call the rest of the handlers
    err = CallNextEventHandler( nextHandler, evt);
    return err;
}

//registers for the Carbon application quit notification
void registerForApplicationQuitNotification()
{
    // install an event handler to tear down some globals on Quit
    static EventHandlerUPP app = NULL;
    EventTypeSpec list[] = {
        {kEventClassApplication, kEventAppQuit},
    };
    app = NewEventHandlerUPP( handleApplicationQuitEvent );
    if (!app)
        return;
    InstallApplicationEventHandler(app, GetEventTypeCount(list), list, NULL, NULL);
}
+1  A: 

If the autorelease pool is the only cleanup you need to do, then no, you don't need to register for a quit notification. Any pool you create is still within the application's address space, which will be freed by the OS when the process terminates.

Also, autorelease pools are typically created per-thread. If your callbacks invoked on different threads, you may need to create a pool per thread. Note that Cocoa also needs to be told that it's operating in a multithreaded environment; see the Threads section of the NSAutoreleasePool reference.

Nicholas Riley
Thanks for the advice about multi-threading, in this case the calls into the plug-in are always on the main thread, so it's only my own threads that need to manage their own autorelease pools.
Rob Keniger
+1  A: 

It's quite likely that NSApplicationLoad sets up NSApplication's first autorelease pool, which will be below yours in the autorelease pool stack (since it was created first). In the background, it will drain this pool and create a new one as it needs to; the first time this happens, your pool goes away, since it was above Cocoa's pool in the stack.

The simple solution is to cut out your global pool and let NSApplication create it. An alternative would be to create and drain local pools within each handler function, particularly if you don't actually need anything from the Application Kit in your plug-in.

Peter Hosey
Thanks, this led me to investigate what happens if I don't create an autorelease pool explicitly and in fact everything works perfectly. It appears that `NSApplicationLoad()` does in fact manage an autorelease pool, although the documentation does not explicitly state this.
Rob Keniger