来自笔尖的 NSWindowController 子类初始化不使用 -initWithCoder:?

Posted

技术标签:

【中文标题】来自笔尖的 NSWindowController 子类初始化不使用 -initWithCoder:?【英文标题】:NSWindowController subclass initialization from a nib doesn't use -initWithCoder:? 【发布时间】:2010-08-15 16:52:09 【问题描述】:

我已将 NSWindowController 的自定义子类添加到我的 Cocoa 项目中,并将我的子类的一个实例添加到我的应用程序的 nib 中。我希望在加载 nib 时看到我对 -initWithCoder: 方法的覆盖,但事实并非如此。为了调试,我添加了一个常规的 -​​init 方法并在其上设置了一个断点——果然,我在加载 nib 时遇到了断点。

这实际上可以使一些事情对我来说更简单(例如设置 windowNibName),但我不明白为什么 Cocoa 会这样。我读过的所有文档都表明 -initWithCoder: 是我应该覆盖的地方。为什么这个案例有什么不同?

【问题讨论】:

为什么要把窗口控制器放到笔尖里?通常,您使用窗口控制器来加载 nib,并且窗口控制器将成为 nib 内容的文件所有者。 这个特殊的窗口控制器用于下拉到我的应用程序主窗口的工作表。通过使其成为它加载的 nib 的文件所有者,以及我的 MainMenu nib 中的***对象,我可以轻松地从任一位置建立到它的 IB 连接。它就像一个魅力。 :-) 【参考方案1】:

我假设要在 Interface Builder 中实例化您的窗口控制器,您将一个通用的 NSObject 实例拖到 nib 文件中,然后将您的自定义 NSWindowController 子类分配为对象的类,对吗?如果是这样,那么我认为它们的主要区别在于您正在处理实例化通用对象而不是 IB 调色板之一中包含的自定义对象。

大多数情况下,当您使用 IB 创建和配置对象时,您在各种检查器中指定的设置会在保存 nib 文件时使用encodeWithCoder: 方法进行编码。然后,当您在应用程序中加载该 nib 文件时,这些对象将使用 initWithCoder: 方法进行初始化。

但是,在该通用对象实例的情况下,Interface Builder 不一定知道被实例化对象的类的任何信息。由于您可以指定任何要实例化的类名,因此如果您指定了一个 IB 没有通过调色板或框架加载的类,它就无法使用NSCoding 序列化该对象。所以我相信当你实例化一个像这样的通用对象时,它会使用init 而不是initWithCoder: 进行初始化,因为在保存 nib 文件时它没有使用encodeWithCoder: 进行保存。

我不知道这是否在任何地方都有记录,但我认为这就是您在那里看到差异的原因。我也不认为它是特定于 NSWindowController 的,而是在 IB 中实例化为通用 NSObject 的任何对象都会看到相同的行为,而不管具体的类是什么。

【讨论】:

我几乎按照你的描述做了……除了我在 IB 中使用类浏览器并将我的 NSWindowController 子类的一个实例拖到我的 nib 的顶层。正如您所描述的,这种行为是有意义的,它似乎是一个有用的功能。太糟糕了,没有更好的记录!【参考方案2】:

我仍然没有正式的答案为什么 Cocoa 会这样,但在实际使用中似乎很方便。我已经使用以下 -init 方法定义了一个 NSWindowController 子类,它就像一个魅力。

- (id)init;

    if ((self = [super initWithWindowNibName:@"MumbleMumbleSheet"]) != nil) 
        …
    
    return self;

如果 -initWithCoder: 被调用,我将不得不弄清楚如何履行调用超级 -initWithCoder: 方法的隐含义务,并且仍然获得用于加载的正确 -windowNibName。这种方式更简单。

我仍然希望指向一些文档的指针,这个类是不同的,并解释了为什么以及如何......但是在没有文档的情况下有经验证据。

【讨论】:

一个原因是 NSWindowController 不需要 xib。您实际上可以使用 IB 创建一个 NSWindowController 并将其窗口插座连接到您想要的任何窗口并“控制”它。但你是对的。我发现自己一直在做和你一样的事情。在 ios 上,视图控制器有一个约定:如果您的视图控制器类名为 XYZViewController,则在初始化时它会自动加载一个名为 XYZView(或类似名称)的 xib 文件。【参考方案3】:

编码器方法用于已序列化并保存到文件的类。

你在这里做的是不同的。您正在将控制器类构建到可执行文件中。这意味着无需从文件中读取类本身,因为它是正在运行的应用程序二进制文件的一部分。

使用此控制器类时,您需要提供一个 init 方法,您可以在其中提供 nib 文件名。为什么?好吧,您将编译的类作为 exe 的一部分,但不知道 nib 文件是什么。这就是您提供这些知识的方式。

这样想。您的控制器类是 exe 的一部分。需要在它和 nib 文件之间建立一些链接。一种方法是扫描所有 nib 文件以查找对该控制器的引用。那将是低效的。在 init 中提供名称和所有引导程序。

换句话说,您从实验中学到了一些重要的经验。干得好,观察力如此之好。

【讨论】:

我应该说得更清楚一点:我正在从一个 nib 加载我的 NSWindowController 子类的这个实例(嗯,现在是 xib)。如果我们可以在此处包含图像,那就太好了,因为这样可以更轻松地描述准确的位置……

以上是关于来自笔尖的 NSWindowController 子类初始化不使用 -initWithCoder:?的主要内容,如果未能解决你的问题,请参考以下文章

来自 NSWindowController Cocoa 的自定义视图

如果我加载一个笔尖而不将 xib 中的文件所有者作为 -loadNibNamed 中的所有者传递:所有者:

来自笔尖的IOS loadingView

来自 NSString 的 UITableViewCell 笔尖

swift 来自笔尖的UIView

来自笔尖的自定义 UiTableViewCell 没有重用?