在看到What is responsible for releasing NSWindowController objects?
之后,我正在寻求进一步的澄清我正在为我的侄子写一个简单的库存管理应用程序。我有一个表格视图,显示其“库”等的内容。要向库中添加新项目,他们会单击“+”按钮。此按钮将打开一个新窗口,提示他们输入项目的详细信息,并在单击“确定”时验证输入。
所有这一切都很好。但是,我对内存管理有疑问。要创建新窗口,我使用以下代码:
- (IBAction)addNewItem:(id)sender {
LibraryItemEditorController *editorController =
[[LibraryItemEditorController alloc]
initWithWindowNibName:@"LibraryItemEditor"];
[editorController showWindow:nil];
// editorController is "leaked" here, it seems.
}
我无法在editorController
的末尾发布(也不自动发布)addNewItem:
,因为没有其他内容引用editorController
;如果我释放它,窗口会立即消失。但是,我希望窗口控制器在窗口关闭后释放。在Apple的Window Programming Guide中,我阅读了以下内容:
如果要关闭窗口 制作窗口和窗口控制器 当它不属于某个时候就会消失 文档,你的子类
NSWindowController
可以观察到NSWindowWillCloseNotification
或者,作为 窗口委托,实现windowWillClose:
方法和包含 您的以下代码行 实现:[self autorelease];
我在窗口控制器的[self autorelease]
方法中使用了windowWillClose:
。这样做,并且不会泄漏内存。然而,它只是感觉丑陋; addNewItem:
看起来像是在泄漏记忆,而静态分析也是如此。 我知道它实际上是在windowDidClose:
中处理的,但它感觉不对。此外,窗口控制器现在正在释放自身,而没有保留自己。这一切都违背了我所学到的内存管理规则。
我的另一个选择是在父控制器(NSWindowController
或NSMutableSet
NSWindowController
s)上放置一个ivar,然后注意NSWindowWillCloseNotification
中的NSWindowDidCloseNotification
父控制器并将其作为响应发布。这更清洁,可能就是我要做的。不过,这也是一项相当多的工作,这引出了我的问题。
正在关注NSWindowControllers
标准的做法吗?管理[self autorelease]
按需创建和销毁的标准方法是什么? {{1}}方式是传统推荐的选项吗?现在我们只有静态分析这是一个问题吗?
答案 0 :(得分:5)
听起来你的窗户是模态的,在这种情况下:
[NSApp runModalForWindow:[editorController window]];
[editorController release];
这是非模态窗口的一种模式:
@implementation QLPrefWindowController
+ (id) sharedInstance
{
if (!_sharedInstance)
{
_sharedInstance = [[QLPrefWindowController alloc] init];
}
return _sharedInstance;
}
- (void)windowWillClose:(NSNotification *)notification
{
if ([notification object] == [self window] && self == _sharedInstance)
{
_sharedInstance = nil;
[self release];
}
}
然后,任何想要访问或显示窗口的人都可以通过+sharedInstance
类方法执行此操作。如果窗口不可见,则创建它,否则它们将获得当前可见的窗口。
答案 1 :(得分:0)
上面发布的非模态情况的解决方案不正确,因为类方法无法访问iVars。我通过创建一个类方法(在我的NSWindowController子类中称为LPWindowController)解决了这个问题,如下所示:
+ (id) autoreleasingInstanceShowingWindow
{
LPWindowController *obj = [[LPWindowController alloc] initWithWindowNibName:@"myWindow"];
[obj showWindow:[NSApp delegate]];
return obj;
}
上面的方法返回一个保留计数为1的LPWindowController实例。它还显示了控制器的窗口。这很重要,因为否则我们将不得不依赖调用者调用“showWindow:”来使LPWindowController的窗口出现。如果调用者没有做到这一点(这可能是一个错误),控制器永远不会被释放。通过在我们分配/初始化控制器时强制窗口显示,我们避免了这个陷阱。
接下来,在IB中,我们将窗口的委托设置为LPWindowController类,并将其添加到该类中:
- (void) windowWillClose:(NSNotification *)notification
{
[self autorelease];
}
最后,当我们需要创建并显示我们的窗口时,我们只需使用下面的方法,而不是在LPWindowController上调用“alloc / initWithWindowNibName:”。
LPWindowController *cont = [LPWindowController autoreleasingInstanceShowingWindow];
cont = nil;
第二行非常重要。首先,它消除了关于“未使用的变量cont”的警告。其次,它消除了悬空指针的危险。一旦LPWindowController的实例自行释放,如果未填写cont,它将指向垃圾内存。
无论如何,这是我推荐的解决这个问题的方法。