It seems that waiting for me to write a new blog post is like waiting for Larry David to write a new TV show. By the way, season one of Lap Cat Software Blog is available now on DVD for the low price of free ($35 shipping and handling). For those of you who don’t care for reruns, the wait is finally over: today is the season two premiere blog post! If you recall, in last season’s finale I was abducted by aliens and replaced by an evil clone. As we begin this season, I’m still the clone, but I’ve mellowed and become Lieutenant Governor. Confused?
I was confused about calling
-[NSObject performSelector:withObject:afterDelay:] during application launch. Specifically, I was interested in a delay of
(NSTimeInterval)0.0. When exactly, if ever, would the selector be performed? Is there a chance that user events could intervene? According to the documentation,
The run loop for the current thread must already be running and in the default mode ( For the purposes of investigation, I created an Xcode test project. In typical overkill fashion, I decided that getting to the bottom of the issue would require overriding every public method of
aSelector to be sent.
NSWindow, along with some selected super and protocol methods. I threw in
NSAutoreleasePool for good measure. In my
main(), I put the following commands before the call to
[[JJApplication class] poseAsClass:[NSApplication class]];
[[JJAutoreleasePool class] poseAsClass:[NSAutoreleasePool class]];
[[JJMenu class] poseAsClass:[NSMenu class]];
[[JJRunLoop class] poseAsClass:[NSRunLoop class]];
[[JJThread class] poseAsClass:[NSThread class]];
[[JJWindow class] poseAsClass:[NSWindow class]];
There was probably an easier way to do this, but I did what I did. I yam what I yam. You can download my project. As usual, it’s released under the SHAG license.
Leaving out many details, here is the basic sequence of events when you launch a Cocoa application. Your results may differ, so talk to your doctor before betting your life on this sequence.
-[NSApplication run]. This is the point of no return. That is, you never return from this method.
-[NSApplication makeWindowsPerform:@selector(_registerDragTypesIfNeeded) inOrder:NO].
- Return from
-[NSApplication nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantFuture] inMode:kCFRunLoopDefaultMode dequeue:YES].
NSApplicationDidFinishLaunchingNotification. Notice that the app already returned from
finishLaunching a while ago (relatively speaking).
-[NSApplication makeWindowsPerform:@selector(_visibleAndCanBecomeKey) inOrder:NO].
NSEvent with type
-[NSApplication sendEvent:] with the same event.
- Return from
A few interesting notes:
NSThread. As you would expect, all of the application’s methods are called on the one, main thread. I’ve defined the macro
JJTHREADLOGGING to verify this. For the main thread, which is returned by
+[NSThread currentThread], none of the methods
+[NSThread allocWithZone:], or
-[NSThread init] are ever called.
NSAutoreleasePool. An autorelease pool is not created until after
-[NSApplication finishLaunching] returns. If you call
autorelease in your application delegate method
applicationWillFinishLaunching:, an autorelease pool will not be created! Memory leak, anyone? The methods
+[NSAutoreleasePool allocWithZone:] and
-[NSAutoreleasePool init] are called many times, but they always return the same object, while
-[NSAutoreleasePool release] and
-[NSAutoreleasePool dealloc] are never called.
NSRunLoop. The run loop for the main thread is created in
-[NSApplication setServicesMenu:]. Strangely, the run methods for the run loop (
-[NSRunLoop acceptInputForMode:beforeDate:], -[NSRunLoop limitDateForMode:], -[NSRunLoop run], -[NSRunLoop runMode:beforeDate:], -[NSRunLoop runUntilDate:] ) are never called.
If you’d like to try my project for yourself or modify it, I’d be very interested to hear about what you learn. I haven’t really investigated application termination, for example. You may have noticed that I also haven’t addressed how
-[NSObject performSelector:withObject:afterDelay:] works during launch. I’m such a tease! These questions — and many others — will be answered in the next episode.