【问题标题】:Is there a reliable way to destroy private data structures when a standard NSView or NSWindow is destroyed?当标准 NSView 或 NSWindow 被销毁时,是否有可靠的方法来销毁私有数据结构?
【发布时间】:2015-04-08 18:35:42
【问题描述】:

我正在为另一种编程语言开发一个 GUI 框架,它可以让我以本地后端为目标,即 Windows 上的 Windows API、Mac OS X 上的 Cocoa 和其他 Unix 系统上的 GTK+。框架的实际内容仍然是 C 和 Objective-C。我知道我正在做的事情与正常的 Cocoa 方式相反,但我不知道其他平台有更好的解决方案。

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

我不确定如何使用 Cocoa 来处理这个问题。我知道控制器/视图关系应该是相反的。我知道有了这样的关系,我的显式销毁函数很容易,但是对我来说,父对象的自动销毁并不明显。

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

这是我在 10.9 上通过单击标题栏中的关闭按钮关闭窗口(有两个按钮和一个不受 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

如您所见,没有任何自定义控件与 NSWindow 一起被销毁。

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

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

感谢理解。

【问题讨论】:

    标签: objective-c cocoa object-lifetime


    【解决方案1】:

    如果窗口由窗口控制器(NSWindowController 或子类的实例)控制,则setReleasedWhenClosed:YES 无效。窗口控制器对其窗口有一个强引用。

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

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

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

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

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

    如果您确实响应了此类通知或方法调用并进行了不可逆的清理,请务必清除相关属性,以免重复。比如设置delegate为nil,移除observers等

    【讨论】:

    • 嗯,我现在不直接使用控制器对象。如果新的超级视图是nil,我想我可以在viewDidMoveToSuperview 中处理清理工作;这可能正是我正在寻找的,但我需要先测试。我已经在windowWillClose: 中进行了窗口清理,并且效果很好(它们是上面我的 alloc/free 日志中释放的唯一对象),但我忽略了将委托设置为nil;感谢您指出了这一点。在此感谢。
    • 老实说,我不介意视图本身是否因为 Cocoa 中的其他一些我不知道的东西而保持活力;我想我从来没有想过签入viewDidMoveToSuperview。 (我也可以将视图移动到一个特殊的“已删除视图”超级视图,我同样在 application[Should|Will]Terminate: 或类似的地方发布......)
    猜你喜欢
    • 2011-02-23
    • 1970-01-01
    • 2017-07-07
    • 2016-11-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多