当标准的 NSView 或 NSWindow 被销毁时,是否有可靠的方法来销毁私有数据结构?

Is there a reliable way to destroy private data structures when a standard NSView or NSWindow is destroyed?

我正在为另一种编程语言开发一个 GUI 框架,它可以让我定位本机后端,即 Windows、[=59= 上的 Windows API ] 在 Mac OS X 上,以及在其他 Unix 系统上的 GTK+。该框架的真正核心仍将在 C 和 Objective-C 中。我知道我正在做的事情与正常的 Cocoa 方式相反,但我不知道其他平台有更好的解决方案。

对于每个 UI object(windows、按钮等),我需要一定数量的私有数据结构,通常是一两个。您可以将这些视为控制器的数据字段object。有函数 uiWindowDestroy()uiControlDestroy() 可以立即销毁 window/control,释放进程中的数据结构。 (在 Objective-C 的情况下,释放控件是通过调用 release 实现的。)当它们所在的 window 关闭时,数据结构也应该被释放(不仅仅是 hidden/ordered out) 或 window/control 它们是 child 的被显式销毁。在 Windows 上,我通过处理 WM_DESTROY 来做到这一点;在 GTK+ 上,我通过连接到 "destroy" 来做到这一点。这两个选项都可以处理这两种情况。

我不确定如何用 Cocoa 处理这个问题。我知道 controller/view 关系应该是相反的。我知道有了这样的关系,我的 explicit-destruction 功能很简单,但是 parent object 的自动销毁对我来说并不明显。

现在,这就是我尝试过的方法:我的 NSWindow 使用自定义 NSView 子类作为内容视图,它在 dealloc 中触发其 children 销毁。我还将 NSButton 子类化以释放 dealloc 中的数据结构。最后,我的 NSWindow 调用了 setReleasedWhenClosed:YES,window 委托在 windowWillClose: 中调用了 [self relase]。所有这些 (sub)类 在 allocdealloc 中都有特殊代码,打印出这样的 allocation/deallocation 正在发生。

这是我在 10.9 上通过单击标题栏中的关闭按钮关闭 window(它有两个按钮和一个不受 NSView 支持的伪控件)时得到的结果:

0x7fce53c1a030 alloc uiWindow            (window private data; malloc())
0x7fce53c3d670 alloc uiContainer         (window content view; NSView)
0x7fce53c0eed0 alloc uiWindowDelegate    (window delegate; NSObject)
0x7fce53c0f770 alloc stack               (viewless control; malloc())
0x7fce53c195f0 alloc uiSingleViewControl (button private data; malloc())
0x7fce53c0abb0 alloc uiSingleViewControl (button private data; malloc())
0x7fce53c3d0e0 alloc uiControl *[]       (viewless control private data; malloc())
0x7fce53c1b8b0 alloc int[]               (same)
0x7fce53c0ed10 alloc intmax_t[]          (same)
0x7fce53c3bda0 alloc intmax_t[]          (same)
0x7fce53c1a030 free
0x7fce53c0eed0 free

如您所见,none 个自定义控件与 NSWindow 一起被销毁。

我发现 this question 这表明在应用程序终止的情况下,默认的自动释放池不会被耗尽;我尝试将各种 release 调用包装在 @autoreleasepool 块中,但也没有用;输出是一样的。

所以我想知道是否有一种可靠的方法来做我想做的事情。我知道当 object 被释放时没有发送通知,也没有办法监视,我很困惑为什么我的助手 Objective-C object 没有被释放。理想情况下,我会完全避免对 NSButton 和其他类进行子类化...

谢谢理解。

如果 window 由 window 控制器(NSWindowController 的实例或子类)控制,则 setReleasedWhenClosed:YES 无效。 window 控制器对其 window.

有很强的引用

同样,视图控制器对其视图有很强的引用。

如果您想在 window 关闭时释放它,请务必在 -windowWillClose: 中释放 window 控制器或以其他方式响应 NSWindowWillCloseNotification 通知。同样,如果你有视图控制器,你应该在使用完它们后释放它们(而不是它们控制的视图)。

尽管如此,您通常不应将控制代码用于内存管理。因为您不知道除了您的代码之外还有什么可能维护对对象的强引用,您无法知道释放您的引用会导致对象被释放。

相反,您应该将此类代码放在通知处理程序或委托方法中,例如 -windowWillClose:NSWindowWillCloseNotification

对于视图,您可以覆盖 -view[Will|Did]MoveTo[Window|Superview][:]

如果以及当您响应此类通知或方法调用并进行不可逆清理时,请务必清除相关属性,以免重复发生。例如,将委托设置为nil、移除观察者等