【问题标题】:Designated Initializer, clarify please.指定初始化器,请澄清。
【发布时间】:2015-11-24 08:05:48
【问题描述】:

现在这个问题困扰了我很长一段时间,我真的需要有人为我澄清这个问题。

1) 当一个指定的初始化方法被一个子类的 init 方法调用时,编译器如何确定它?据说是覆盖率最高的那个。

2) 当使用多个init方法进行子类化时,每个类都有自己指定的初始化方法吗?对问题 2 的回答也有助于理解这一点。

3) 当多个类确实有一个指定的初始化器时,其他 init 方法如何在它们的类中调用指定的初始化器?为什么他们也不调用任何其他初始化程序?为什么指定初始化器?它有什么特别之处?

【问题讨论】:

    标签: objective-c initialization subclass init designated-initializer


    【解决方案1】:

    在 Objective-C 中,类的指定初始化器是负责正确初始化类以使其处于适当状态以供使用的方法。代码中没有任何内容将其标记为指定的初始化程序,因此通常在头文件中有一个注释。由使用或扩展该类的开发人员确定哪个方法是指定的初始化程序(通常是init 或以init 为前缀)并相应地编写代码。如果一个类没有正确记录并且它的源代码不可用,这可能会导致类的误用,这是 Swift 试图解决的缺点之一。因此,为了解决您的问题...

    1. 编译器无法确定指定的初始化程序。子类还应该有一个指定的初始化器,它会在某个时候调用父类的指定初始化器。
    2. 每个类都应该清楚地说明(通过 cmets 或文档)哪个初始化器打算用作指定的初始化器。由使用该类的开发人员确保调用指定的初始化程序。由类本身的开发人员来确保按预期调用超的指定初始化程序。但是,对于正确编写的类,任何 init 方法应该调用指定的初始化程序。但是,编译器不保证。
    3. 其他初始化方法需要适当编码以通过[self init...][super init...] 调用指定的初始化程序。同样,由您决定如何使用一个类并适当地使用或扩展它。

    指定的初始化程序是执行“繁重”工作以准备类的新实例以供使用的方法。其他初始化程序称为便利初始化程序,通常用于提供具有隐含默认值的较短签名,因为您无法为 Objective-C 方法签名指定默认参数值。

    在大多数情况下,如果一个类编写得当,您不必太担心,因为所有便利初始化程序最终都会调用指定的。

    在 Swift 的开发过程中对此进行了很多思考,即使您不打算学习它,也应该阅读Swift Initializers,因为它可以让您深入了解正确的初始化链然后,您可以使用它来指导您在 Objective-C 中创建初始化程序。

    更新: 从 Xcode 6 开始,指定的初始化程序可以这样标记,通常通过 NS_DESIGNATED_INITIALIZER 宏。这有助于强制执行正确编写的类,并且是从 Swift 带回 Objective-C 的东西。查看iOS Designated Initializers : Using NS_DESIGNATED_INITIALIZER

    【讨论】:

    • 如果我不调用 DI 而只是使用便利初始化器在子类中直接调用 [super init] 或 [super init...] 而不是首先调用 DI,那会怎样?还有你的意思是什么>“因为你不能为Objective-C方法签名指定默认参数值。” P.s 解释得很好。
    • 便利构造器的每个实现都应该调用它自己的 DI(如果有的话),或者它的 super 的。所以是的,你应该这样做。我从来不需要调用多个初始化器来使用一个类。至于默认参数,如果没有给出,则不能指定要使用的默认值。其他语言允许使用 func foo (string name, string category = "none") 之类的内容,因此如果您不指定值,则会分配默认值。
    【解决方案2】:
    1. 编译器不会以任何方式检测到它。为了让编译器知道是否指定了初始化程序,您必须使用NS_DESIGNATED_INITIALIZER 宏对其进行注释。

    2. 如果一个类有一个初始化器,它也总是有一个指定的初始化器。它实际上可以有多个指定的初始化器。你根本不可能有一个没有指定初始化程序的类。指定和便利之间的区别非常简单。指定初始化器调用[super init...](调用超类初始化器),而便利调用[self init...](调用同一类的另一个初始化器)。

    3. 便利初始化程序使用[self init...] 调用指定的初始化程序。指定的初始化器并不特殊。它们实际上是最基本的初始化器。他们做了两件事——他们初始化父级(使用[super init...]),然后初始化需要的东西(例如属性的默认值等)。便利构造器是一种特殊的构造器——它们是指定构造器的扩展。他们调用指定的初始化器并做更多的事情。例如-initWithStyle: 将是一个指定的初始化器,它将一个对象初始化为特定的样式。 -initWithStyle:andFrame: 将执行相同的操作,但它还将 frame 属性设置为特定值。如果-initWithStyle:andFrame: 是使用[self initWithStyle:... andFrame:...] 实现的,那么它就是一个便利的初始化器。

    【讨论】:

    • 既然 -initWithStyle:andFrame: 参数最多,那岂不是指定的初始化器而不是方便的初始化器,不是吗?
    • @shinninja 那只取决于实现,我们真的不知道......两种方式都可以。
    • 那么指定的初始化器不一定是参数最多的初始化器?它的参数可以比便利初始化器少吗?
    • @shinninja 是的,参数的数量并不重要。唯一重要的是初始化程序是调用super.init(...) 还是self.init。你是对的,通常有便利的初始化器为指定初始化器的参数提供默认值,因此它们的参数较少,但这不是规则。
    猜你喜欢
    • 2023-04-07
    • 2015-03-20
    • 2023-04-09
    • 1970-01-01
    • 1970-01-01
    • 2014-07-27
    • 2020-08-10
    • 2015-02-24
    • 1970-01-01
    相关资源
    最近更新 更多