16

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

苹果称,

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

但 Apple 也表示,一个类应该有一个指定的初始值设定项。

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

4

1 回答 1

28

苹果说:

指定初始化程序 init... 方法,主要负责初始化类的新实例。每个类定义或继承其自己指定的初始化程序。通过消息给self,同一个类中的其他init...方法直接或间接调用指定初始化器,而指定初始化器通过消息给super调用其超类的指定初始化器。[添加雇员]

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

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

例如,UIView 的指定初始化程序是initWithFrame:. 所以,UIViewinitWithCoder看起来像:

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

指定初始化器的重点是创建一个所有初始化都必须经过的中心点,以确保每个实例都完全初始化,而不管数据来自哪里或初始化的情况。

这绝不应该被认为意味着一个类只能有一个初始化方法。

编辑01

来自评论:

特别是,当通过 initWithCoder 进行初始化时,如何为我的一些 ivars 传递值?

好吧,你没有。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最终会成为并行指定初始化程序,因为它会像指定初始化程序一样处理所有初始化。它看起来不像指定的初始化器,因为它的所有数据都由编码器提供,但它执行相同的功能。

这是理论和实践不能很好结合的案例之一。“指定初始化程序”概念实际上只适用于您从头开始创建实例的情况。

于 2010-05-31T17:06:30.270 回答