【问题标题】:Why use required Initializers in Swift classes?为什么在 Swift 类中使用必需的初始化器?
【发布时间】:2016-08-26 03:15:35
【问题描述】:

我正在尝试了解 required 关键字在 Swift 类中的用法。

class SomeClass 
{
    required init() {
        // initializer implementation goes here
    }
}

required 不会强迫我在我的子类中实现该方法。如果我想重写父类的required 指定初始化程序,我需要编写required 而不是override。我知道它是如何工作的,但不明白我为什么要这样做。

required 有什么好处? 据我所知,像 C# 这样的语言没有这样的东西,并且可以在 override 上正常工作。

【问题讨论】:

  • 由于继承,不需要在子类中实现,这是有道理的。但是我也想知道这里的好处。如果一个子类(假设覆盖init(),但不是一些必需的初始化程序)被一个调用继承的必需初始化程序的进程初始化,并且该必需的初始化程序随后调用init(),它不会调用子类的重写init和所有很好? -- 我认为唯一的好理由是所需的初始化程序在 self 上调用 init(),因此永远不会调用覆盖 'init()'。
  • 在这种情况下可能会很有趣:Protocol func returning Self.

标签: swift initialization


【解决方案1】:

这实际上只是满足编译器的一种方式,以确保如果这个类有任何子类,它们将继承或实现相同的初始化程序。这点值得怀疑,因为如果子类有自己的指定初始化器,则不会继承父类的初始化器。因此,超类有可能有一个初始化器,而子类没有有它。 required 克服了这种可能性。

需要以这种方式满足编译器的一种情况涉及协议,其工作方式如下:

protocol Flier {
    init()
}
class Bird: Flier {
    init() {} // compile error
}

问题是,如果 Bird 有一个子类,则该子类必须实现或继承 init,而您不能保证这一点。将 Bird 的 init 标记为 required 可以保证这一点。

或者,您可以将 Bird 标记为 final,从而保证反过来,即它永远不会有子类。

另一种情况是你有一个工厂方法,它可以通过调用相同的初始化程序来创建一个类或其子类:

class Dog {
    var name: String
    init(name: String) {
        self.name = name
    }
}

class NoisyDog: Dog {

}

func dogMakerAndNamer(whattype: Dog.Type) -> Dog {
    let d = whattype.init(name: "Fido") // compile error
    return d
}

dogMakerAndNamer 正在调用 Dog 或 Dog 子类的 init(name:) 初始化程序。但是编译器如何确定一个子类会有一个init(name:) 初始化器呢? required 名称可以平息编译器的恐惧。

【讨论】:

  • 所以如果“NoisyDog”有“let noise: String”,你会在“required init(name: String)”中做什么?将 var 更改为 'var noise: String!' 的“正确”方法是什么?并希望如果有人像上面那样使用 Dog.Type 初始化,他们也知道要在之后设置“噪声”变量吗?或者您是否必须提供一些自定义初始化程序替换的默认值,例如 'var noise = "Arf!"'?我在这里寻找最佳实践(这似乎是一个考虑不周的语言结构,它打破了许多标准的 OO 模式,但我知道 Swift 的设计者比这更好,所以一定有一个好方法)。
  • 或者,是否应该很少使用“必需”的“最佳实践”?
  • 唯一一次你会说required 是编译器强迫它。编译器会为您服务,您将使用上述解决方案之一。否则,不要使用它。
  • 第一个例子中的子类如何不继承 init() ?
  • @ArtiomLemiasheuski 如果子类也声明了一个初始化器。当然可能不会,但编译器无法提前知道。
【解决方案2】:

我想提请注意Required 提供的另一个解决方案,除了上面给出的马特。

class superClass{
    var name: String
    required init(){
        // initializer implementation goes here
        self.name = "Untitled"
    }
}
class subClass: superClass {
    var neakName: String = "Subclass Untitled"

}
let instanceSubClass = subClass()
instanceSubClass.name        //output: "Untitled"
instanceSubClass.neakName    //output: "Subclass Untitled"

正如你可以在上面的例子中看到的,我在superClass上声明了required init(),超类的init()初始化器默认继承在subClass上,所以你可以创建一个子类let instanceSubClass = subClass()的实例.

但是,假设您想在子类上添加一个指定的初始化程序,以将运行时值分配给存储的属性neakName。当然你可以添加它,但这会导致超类的初始化器不会被继承到子类,所以如果你要创建一个subClass的实例,你将通过它自己指定的初始化器来创建以下。

class superClass{
    var name: String
    init(){
        // initializer implementation goes here
        self.name = "Untitled"
    }
}
class subClass: superClass {
    var neakName: String = "Subclass Untitled"
    init(neakName: String) {
        self.neakName = neakName
    }
}
let instanceSubClass = subClass(neakName: "Bobby")
instanceSubClass.name       //output: "Untitled"
instanceSubClass.neakName   //output: "Bobby"

在上面,您将无法仅通过 subClass() 创建 subClass 的实例,但是如果您希望 superClass 的每个子类都必须有自己的 init() 初始化器通过subClass() 创建直接实例。只需在超类上的init() 之前放置required 关键字,它会强制您在subClass 上添加init() 初始化程序 - 如下所示。

class superClass{
    var name: String
    required init(){
        // initializer implementation goes here
        self.name = "Untitled"
    }
}
class subClass: superClass {
    var neakName: String = "Subclass Untitled"
    init(neakName: String) {
        self.neakName = neakName
    }
}    // Compiler error <------------ required `init()` must be provided by subClass.
let instanceSubClass = subClass(neakName: "Bobby")
instanceSubClass.name       //output: "Untitled"
instanceSubClass.neakName   //output: "Bobby"  

所以,在超类的初始化程序之前使用 required 关键字,当您希望所有子类都必须已实现超类的 required initializer 时。

【讨论】:

  • 这没有意义。然后,要解决这个问题,您需要在子类上提供一个 init(),这会使子类 var 未设置。这导致了很多无法维护的代码,依赖 var ...!和运行时错误,如果曾经使用过“必需”的 init 而没有回来并清理那些“将在使用前初始化”的变量,而不是 let ...好多了。
【解决方案3】:

根据documentation

Write the required modifier before the definition of a class initializer to
indicate that every subclass of the class must implement that initializer

所以是的,required 确实会强制所有子类实现这个构造函数。但是,这不是必需的

 if you can satisfy the requirement with an inherited initializer.

因此,如果您创建了无法使用父构造函数完全初始化的更复杂的类,则必须实现 require 构造函数。

文档中的示例(添加了一些内容):

class SomeClass {
    required init() {
        // initializer implementation goes here
    }
}

class SomeSubclass: SomeClass {
    let thisNeedsToBeInitialized: String
    required init() {
        // subclass implementation of the required initializer goes here
        self.thisNeedsToBeInitialized = "default value"
    }
}

【讨论】:

    【解决方案4】:

    如果您尝试在子类中添加自己的初始化程序,那么您必须遵循在超类中声明的某些内容。因此,它确保您不会忘记实现所需的方法。如果你忘记了编译器会给你错误// fatal error, we've not included the required init() 。另一个原因是它创建了一组条件,任何子类都应该遵循它,子类正在定义自己的初始化器。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-11-26
      • 1970-01-01
      • 1970-01-01
      • 2021-01-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多