我在我的Cocoa项目中添加了一个自定义的NSWindowController子类,并将我的子类的一个实例添加到我的应用程序的nib中。我希望在加载nib时看到我对-initWithCoder:方法的覆盖,但事实并非如此。为了调试,我添加了一个常规的-init方法并在其上设置断点 - 当然,我在加载nib时点击了断点。
这实际上可以让一些事情变得更简单(例如设置windowNibName),但我不明白为什么Cocoa会以这种方式运行。我读过的所有文档都表明-initWithCoder:我应该重写的地方。为什么这个案例有所不同?
答案 0 :(得分:4)
我假设在Interface Builder中实例化你的窗口控制器,你将一个通用的NSObject
实例拖到nib文件中,然后将你的自定义NSWindowController
子类指定为对象的类,这是正确的?如果是这样,那么我认为它们的主要区别在于你正在处理实例化一个通用对象而不是IB调色板中包含的自定义对象。
大多数情况下,当您使用IB创建和配置对象时,在保存nib文件时,使用encodeWithCoder:
方法对您在各种检查器中指定的设置进行编码。然后在应用程序中加载该nib文件时,使用initWithCoder:
方法初始化这些对象。
但是,在该通用对象实例的情况下,Interface Builder不一定知道有关要实例化的对象的类的任何信息。由于您可以指定要实例化的任何类名,如果您指定IB没有通过调色板或框架加载的类,则无法使用NSCoding
序列化该对象。所以我相信当你实例化这样的通用对象时,它会使用init
而不是initWithCoder:
进行初始化,因为当nib文件是nib文件时,它首先没有使用encodeWithCoder:
保存。保存。
我不知道这是否记录在任何地方,但我认为这就是为什么你会看到那里的差异。我也不认为它特定于NSWindowController
,而是你会看到IB中实例化为通用NSObject
的任何对象的相同行为,无论具体的类如何。
答案 1 :(得分:0)
我仍然没有正式回答为什么 Cocoa以这种方式行事,但在实际使用中似乎很方便。我已经使用以下-init方法定义了一个NSWindowController子类,它就像一个魅力。
- (id)init;
{
if ((self = [super initWithWindowNibName:@"MumbleMumbleSheet"]) != nil) {
…
}
return self;
}
如果-initWithCoder:被调用,我将不得不弄清楚如何实现调用super -initWithCoder:方法的隐含义务,并仍然获得用于加载的右-windowNibName。这种方式更直接。
我仍然很欣赏指向所说这个类不同的文档的指针,并解释了为什么以及如何...但是在没有文档的情况下有经验证据。
答案 2 :(得分:0)
编码器方法用于已序列化并保存到文件的类。
你在这里做的是不同的。您正在将控制器类构建到可执行文件中。这意味着不需要从文件中读取类本身,因为它是正在运行的应用程序二进制文件的一部分。
使用此控制器类时,您需要提供一个init方法,您可以在其中提供nib文件名。为什么?好吧,你有编译的类作为你的exe的一部分,但不知道nib文件是什么。这就是你提供这些知识的方式。
这样想。你的控制器类是exe的一部分。需要在它和nib文件之间建立一些链接。一种方法是扫描所有nib文件,寻找对该控制器的引用。那将是低效的。在init和所有bootstraps中提供名称。
换句话说,您从实验中学到了一些重要的经验教训。干得好,因此如此观察。