iPhone —— initWithCoder 是通常指定初始化器设计模式的例外吗?

发布于 2024-09-03 14:11:43 字数 487 浏览 9 评论 0原文

我有一个班级MyClass。它有实例变量passedInVar1、passedInVar2等,它们的值将从请求初始化的对象传入。它还具有实例变量decodedVar1、decodedVar2等,这些变量将从存档中解码——或者如果没有存档则设置为默认值。

根据 Apple,

当一个对象接收到 initWithCoder: 消息时,该对象应该首先向其超类(如果适用)发送消息来初始化继承的实例变量,然后应该解码并初始化自己的实例变量实例变量。

但苹果公司还表示,一个类应该有一个指定的初始值设定项。

处理这一切的最好方法是什么?

I have a class MyClass. It has instance variables passedInVar1, passedInVar2, etc. whose values will be passed in from the object that requests the initialization. It also has instance variables decodedVar1, decodedVar2, etc. that will be decoded from an archive -- or set to a default value if there is no archive.

According to Apple,

When an object receives an initWithCoder: message, the object should first send a message to its superclass (if appropriate) to initialize inherited instance variables, and then it should decode and initialize its own instance variables.

But Apple also says that a class should have a single designated initializer.

What is the best way to deal with all of this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

只是在用心讲痛 2024-09-10 14:11:43

苹果说:

指定初始化器 初始化器...
具有主要责任的方法
用于初始化 a 的新实例
班级。每个类都定义或继承
它自己指定的初始值设定项。
通过向自己、他人发送信息
init...同一个类中的方法
直接或间接调用
指定的初始值设定项,以及
指定的初始值设定项,通过
消息到超级,调用
其指定的初始值设定项
超类。 [已添加雇员]

原则上,指定的初始化程序是所有其他 init 方法调用的一个 init 方法。然而,它并不是唯一的 init 方法。每个类也不必有自己的类。在实践中,更常见的情况是指定的初始化器实际上是超类的 init。

initWithCoder 的主要功能是允许从存档对象进行初始化。对于需要某些特定数据的类,它的指定初始化程序将接受该数据。 initWithCoder 然后简单地解压缩存档,然后调用指定的初始化程序。

例如,UIView 的指定初始化器是 initWithFrame:。因此,UIView 的 initWithCoder 看起来像这样:

- (id)initWithCoder:(NSCoder *)decoder{
    CGRect theFrame= //...uppack frame data
    self=[self initWithFrame:theFrame];
    return self;
}

指定初始化器的要点是创建一个所有初始化都必须经过的中心点,以确保每个实例都完全初始化,无论数据来自何处来自或初始化的情况。

这绝不意味着一个类只能有一个初始化方法。

Edit01

来自评论:

特别是,我如何传递值
对于我的一些伊娃
初始化是通过发生的
initWithCoder?

好吧,你不知道。 initWithCoder 的全部意义在于,您正在处理类的冷冻干燥实例,其中包含重新创建对象所需的所有数据。

NSCoding 协议使您的类的行为就像漫画书中作为“海猴”出售的丰年虾一样。编码方法对丰年虾/实例进行脱水/冷冻干燥。解码方法使丰年虾/实例水合,就像将丰年虾倒入水中一样。就像丰年虾除了水之外拥有开始生存所需的一切一样,保存在磁盘上的编码对象也拥有一旦用编码器初始化后重新创建自身所需的所有数据。

典型的例子是 nib 文件。 nib 文件只是一堆 UI 元素和控制器的冷冻干燥实例。 nib 中的 UIViewController 及其 UIView 将其初始化所需的所有数据编码到 nib 文件的 xml 中。当您直接调用 initFromNib 或使用 IBOutlet 调用时,它会调用每个类的 intiWithCoder: 方法。

如果在冻干时不保存完整的对象,则实例对象的存在不需要未冻干的属性。

您只需在对象初始化后设置这些辅助属性即可。

要内联指定的初始值设定项,只需先解码,然后调用指定的初始值设定项。像这样:

-(id) initWithRequiredValue:(id) someValue otherRequiredValue:(id) anotherValue{
    if (self=[super init]){
        self.requiredProperty=someValue;
        self.anotherRequiredProperty=anotherValue
    }
    return self;
}

如果超类不支持 NSCoder,那么你可以在子类中自己启动它:

- (id)initWithCoder:(NSCoder *)decoder {
    id someDecodedValue=[decoder decodeObjectForKey:@"someValueKey"];
    id someOtherDecodedValue=[decoder decodeObjectForKey:@"someOtherValueKey"];
    self=[self initWithRequiredValue:someDecodedValue otherRequiredValue:someOtherDecodedValue];
    return self;
}

这是最简单的情况。如果 super 本身支持 NSCoding,那么您通常最终会编写一个并行指定初始化程序,如下所示:

- (id)initWithCoder:(NSCoder *)decoder {
    if (self=[super initWithCoder:decoder]){
        id someDecodedValue=[decoder decodeObjectForKey:@"someValueKey"];
        id someOtherDecodedValue=[decoder decodeObjectForKey:@"someOtherValueKey"];
        self.requiredProperty=someDecodedValue;
        self.anotherRequiredProperty=someOtherDecodedValue;
    }
    return self;
}

我认为在大多数情况下, initWithCoder 最终成为并行指定初始化程序,因为它负责所有初始化,就像指定的初始化程序应该。它看起来不像指定的初始化器,因为它的所有数据都是由编码器提供的,但它执行相同的功能。

这是理论与实践不一致的情况之一。 “指定初始化程序”概念实际上仅适用于从头开始创建实例的情况。

Apples says that:

designated initializer The init...
method that has primary responsibility
for initializing new instances of a
class. Each class defines or inherits
its own designated initializer.
Through messages to self, other
init... methods in the same class
directly or indirectly invoke the
designated initializer, and the
designated initializer, through a
message to super, invokes the
designated initializer of its
superclass. [emp added]

In principle, the designated initializer is the one init method that all other init methods call. It is not, however, the only init method. Neither does each class have to have its own. More often in practice the designated initializer is actually the super class' init.

The major function of initWithCoder is to allow for initialization from an archived object. In the case of a class which requires some specific data, it's designated initializer will accept that data. initWithCoder then simply unpacks the archive and then calls the designated initializer.

For example, the designated initializer for UIView is initWithFrame:. So, UIView's initWithCoder looks something like:

- (id)initWithCoder:(NSCoder *)decoder{
    CGRect theFrame= //...uppack frame data
    self=[self initWithFrame:theFrame];
    return self;
}

The point of the designated initializer is to create a central point that all initialization has to pass through in order to ensure that each instances is completely initialized regardless of where the data came from or the circumstances of the initialization.

That should never be taken to mean that a class can only have one initializer method.

Edit01

From comments:

In particular, how do I pass values
for some of my ivars in when
initialization is happening via
initWithCoder?

Well, you don't. The entire point of initWithCoder is that you are dealing with a freeze dried instance of your class that contains all the data necessary to recreate the object.

The NSCoding protocol makes your class behave like the brine shrimp they sell as "Sea Monkeys" in the comic books. The coding methods dehydrates/freeze dries the brine-shrimp/instances. The decoding methods hydrates the brine-shrimp/instances just like pouring the brine shrimp into water does. Just like the the brine-shrimp have everything they need to start living except water, a coded object saved on disk has all the data needed to recreate itself once initialized with the coder.

The canonical example of this is a nib file. A nib file is just a bunch of freeze dried instances of UI elements and controllers. A UIViewController and its UIViews in a nib have all the data they need to initialize themselves coded into the xml of the nib file. When you call initFromNib directly or with an IBOutlet, it calls each class' intiWithCoder: method.

If you don't save the complete object when you freeze dry it, then the attributes that don't get freeze dried aren't needed for the instance object to exist.

You just set those ancillary attributes after the object has been initialized.

To inline the designated initializer, you just decode first and then call the designated initializer. Like so:

-(id) initWithRequiredValue:(id) someValue otherRequiredValue:(id) anotherValue{
    if (self=[super init]){
        self.requiredProperty=someValue;
        self.anotherRequiredProperty=anotherValue
    }
    return self;
}

If the super class does not support NSCoder then you start it yourself in the subclass:

- (id)initWithCoder:(NSCoder *)decoder {
    id someDecodedValue=[decoder decodeObjectForKey:@"someValueKey"];
    id someOtherDecodedValue=[decoder decodeObjectForKey:@"someOtherValueKey"];
    self=[self initWithRequiredValue:someDecodedValue otherRequiredValue:someOtherDecodedValue];
    return self;
}

That's the simplest case. If super itself supports NSCoding, then you usually just end up writing a parallel designated initializer like so:

- (id)initWithCoder:(NSCoder *)decoder {
    if (self=[super initWithCoder:decoder]){
        id someDecodedValue=[decoder decodeObjectForKey:@"someValueKey"];
        id someOtherDecodedValue=[decoder decodeObjectForKey:@"someOtherValueKey"];
        self.requiredProperty=someDecodedValue;
        self.anotherRequiredProperty=someOtherDecodedValue;
    }
    return self;
}

I think in most cases, initWithCoder ends up being a parallel designated initializer because it takes care of all initialization just like the designated initializer should. It doesn't look like the designated initializer because all its data is provided by the coder but it performs the same function.

This is one of those cases where theory and practice don't line up well. The "designated initializer" concept really only applies to cases wherein you create instances from scratch.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文