NSWindowController 的 showWindow 内存泄漏:
我已经为以下泄漏而苦苦挣扎了一段时间。我通过 Instruments 将其范围缩小到以下代码块:
- (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;
}
该块的调用方式如下:
[AppDelegate showNewMessageWindowWithRecipients:[NSArray arrayWithObject:recipient]];
其中 recipient
只是一个 NSString。
这是来自仪器的回溯:
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
是一个 NSMutableArray
在 applicationDidFinishLaunching
中分配/初始化并在 dealloc
中释放代码>方法。
在 NewMessageWindowController
中,我使用以下代码来通知应用程序委托窗口即将关闭,并且不再需要保留控制器:
- (void)windowWillClose:(NSNotification *)notification {
[AppDelegate windowControllerDidFinish:self];
}
应用程序委托的方法如下所示:
- (void)windowControllerDidFinish:(NSWindowController *)controller {
[windowControllers removeObject:controller];
}
之前记录数组之后就是我期望的样子。控制器在窗口关闭之前就在那里,当窗口关闭时它就会被删除。
当我关闭窗户时,仪器检测到泄漏。虽然它是开放的,但一切似乎都很好。值得注意的是,dealloc 正在按预期在 NewMessageWindowController 中被调用。 Leaks 不会将控制器本身报告为问题,相反,泄漏的对象是 NSCFString,它只是源于上面的代码。
构建和分析没有拾取任何内容,并且我很确定我的内存管理在创建/显示窗口控制器/窗口的代码块上没有问题。
奇怪的是,只有使用键盘关闭窗口时才会出现泄漏。如果我单击红色关闭按钮,仪器不会拾取任何内容。
最后,Instruments 并不总是显示负责的代码块。在这些情况下,Instruments 中没有引用我的任何代码 - 它似乎都是 AppKit。同样,只有当我使用键盘关闭窗口(cmd-w)时才会出现这种情况。
有什么想法吗?
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 referenced 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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
在这种情况下我要做的是,使用 Xcode4 中的 Instruments 版本,配置分配工具来记录保留/释放事件。这应该向您展示,对于这个特定的控制器,为什么它的保留计数不会为零。请注意,对于基于鼠标和基于键盘的关闭,可能会执行不同的代码路径。
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.
这是因为当对象被“销毁”时,您不能指望可靠地调用 dealloc - 可能由于某种原因使用键盘比单击 X 更不可能导致立即调用 dealloc?
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?