谁拥有NSWindowController,在标准做法?

时间:2009-09-07 23:04:09

标签: objective-c cocoa

在看到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:中处理的,但它感觉不对。此外,窗口控制器现在正在释放自身,而没有保留自己。这一切都违背了我所学到的内存管理规则。

我的另一个选择是在父控制器(NSWindowControllerNSMutableSet NSWindowController s)上放置一个ivar,然后注意NSWindowWillCloseNotification中的NSWindowDidCloseNotification父控制器并将其作为响应发布。这更清洁,可能就是我要做的。不过,这也是一项相当多的工作,这引出了我的问题。

正在关注NSWindowControllers标准的做法吗?管理[self autorelease]按需创建和销毁的标准方法是什么? {{1}}方式是传统推荐的选项吗?现在我们只有静态分析这是一个问题吗?

2 个答案:

答案 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,它将指向垃圾内存。

无论如何,这是我推荐的解决这个问题的方法。