Just expanding on Julia's answer, since I had to restart my App for another reason than updating, I looked into how Sparkle does it -
Latest version(as of 11/2011) of Sparkle has a project target called finish_installation.app that is included in the Resources dir of the Sparkle framework. Sparkle, running as part of the host App, copies finish_application to the application_support directory and uses launched to run its binary executable like this, passing in the host process id and relaunch path:
NSString *relaunchToolPath = [NSString stringWithFormat:@"%@/finish_installation.app/Contents/MacOS/finish_installation", tempDir];
[NSTask launchedTaskWithLaunchPath: relaunchToolPath arguments:[NSArray arrayWithObjects:pathToRelaunch, [NSString stringWithFormat:@"%d", [[NSProcessInfo processInfo] processIdentifier]], tempDir, relaunch ? @"1" : @"0", nil]];
[NSApp terminate:self];
Seems like with this function, when the parent process quits(?), finish_application's parent becomes launched.
finish_installation waits for the passed in process id to disappear, also has an initial check to see if it's parent is launched (pid=1)
if(getppid() == 1)...
if (GetProcessForPID(parentprocessid, &psn) == procNotFound)...
then launches app with:
[[NSWorkspace sharedWorkspace] openFile: appPath];
Last interesting tidbit: If installation takes a long time, finish_installation changes itself to a foreground process so the user can see that some app is running:
ProcessSerialNumber psn = { 0, kCurrentProcess };
TransformProcessType( &psn, kProcessTransformToForegroundApplication );