I've been struggling with the following leak for a while now. I've narrowed it down through Instruments to the following block of code:
- (NewMessageWindowController *)showNewMessageWindowWithRecipients:(NSArray *)recipients {
NewMessageWindowController * newMessageWindowController = [[NewMessageWindowController alloc] init];
[newMessageWindowController showWindow:self]; // 100% on this line.
[newMessageWindowController.toField setStringValue:[recipients componentsJoinedByString:@","]];
[newMessageWindowController.messageView becomeFirstResponder];
[windowControllers addObject:newMessageWindowController];
[newMessageWindowController release];
return newMessageWindowController;
}
The block is called like this:
[AppDelegate showNewMessageWindowWithRecipients:[NSArray arrayWithObject:recipient]];
Where recipient
is just an NSString.
And here is the backtrace from instruments:
30 Friendz start
29 AppKit NSApplicationMain
28 AppKit -[NSApplication run]
27 AppKit -[NSApplication sendEvent:]
26 AppKit -[NSWindow sendEvent:]
25 AppKit -[NSWindow keyDown:]
24 AppKit forwardMethod
23 Friendz -[FriendzAppDelegate showNewMessageWindowWithRecipients:] /Path/To/FriendzAppDelegate.m:226
22 AppKit -[NSWindowController showWindow:]
21 AppKit -[NSWindow makeKeyAndOrderFront:]
20 AppKit -[NSWindow _makeKeyRegardlessOfVisibility]
19 AppKit -[NSWindow _changeKeyAndMainLimitedOK:]
18 AppKit -[NSWindow becomeKeyWindow]
17 AppKit _NXResetCursorState
16 AppKit +[NSEvent _discardCursorEventsForWindowNumber:criteria:]
15 HIToolbox FlushSpecificEventsFromQueue
14 HIToolbox PullEventsFromWindowServer
13 HIToolbox PullEventsFromWindowServerOnConnection(unsigned int, unsigned char)
12 HIToolbox ConvertPlatformEventRecordAndPostWithOptions(__CGEvent*, _CGSEventRecord const*, short, unsigned char, unsigned char)
11 HIToolbox PostEventToQueueInternal
10 HIToolbox _NotifyEventLoopObservers
9 HIToolbox KeyEventPostedObserver
8 HIToolbox TSMProcessRawKeyCode
7 HIToolbox TSMTranslateKeyEvent
6 HIToolbox GetDataFromUCHRForEvent
5 HIToolbox ConvertEventUniCharsToCharCodes
4 HIToolbox utGetInputSourceScriptInfo
3 CoreFoundation CFLocaleCreateCanonicalLocaleIdentifierFromScriptManagerCodes
2 CoreFoundation CFStringCreateWithCStringNoCopy
1 CoreFoundation __CFStringCreateImmutableFunnel3
0 CoreFoundation _CFRuntimeCreateInstance
windowControllers
is an NSMutableArray
alloc/init'ed in applicationDidFinishLaunching
and released in the dealloc
method.
In the NewMessageWindowController
, I use the following to notify the app delegate that the window is about to close and there's no need to retain the controller any more:
- (void)windowWillClose:(NSNotification *)notification {
[AppDelegate windowControllerDidFinish:self];
}
The app delegate's method looks like this:
- (void)windowControllerDidFinish:(NSWindowController *)controller {
[windowControllers removeObject:controller];
}
Logging the array before and after is how I expect it to be. The controller is in there before the window closes, it's removed when the window has closed.
Instruments picks up the leak when I close the window. Whilst it's open, everything appears to be fine. It's worth noting that dealloc is being called in the NewMessageWindowController as expected. Leaks isn't reporting the controller itself as the problem, instead, the object that's leaking is a NSCFString, it just originates from the code above.
Build and Analyze isn't picking anything up, and I'm pretty sure my memory management is fine on the block of code which creates / shows the window controller / window.
What's weird is, there is only a leak if I use the keyboard to close the window. If I click the click the red close button, Instruments doesn't pick anything up.
Finally, Instruments doesn't always show that block of code to be responsible. In those instances, none of my code is referenc开发者_Go百科ed in Instruments - it appears to be all AppKit. Again, this is only if I use the keyboard to close a window (cmd-w).
Any ideas?
What I would do in this case is, using the version of Instruments in Xcode4, configure the allocations instrument to record retain / release events. That should show you, for this particular controller, why it's retain count is not going to zero. Note that there is likely to be a different codepath executed for a mouse-based and keyboard-based close.
Is this because you can't count on dealloc to be reliably called when an object is "destroyed" - it could be that using the keyboard for some reason is less likely to result in an immediate call to dealloc than clicking on the X?
精彩评论