1

我在这里阅读了对类似问题的回复,但我仍然对这里的最佳实施方式、原因以及有什么不同感到困惑。

在这个实现中,我有两个类的初始化器

class Person1 {
  var name: String
  var age: Int
  var nationality: String

  init(name: String, age: Int,  nationality: String) {
    self.name = name
    self.age = age
    self.nationality = nationality
  }

  init(name:String, age: Int) {
    self.name = name
    self.age = age
    self.nationality = "Canadian"
  }
}

创建实例时,我可以选择其中一个。如果我选择第二个,我将默认国籍设置为加拿大

  let p1 = Person1(name: "Stewart", age: 40)

如果我用下面的便利初始化器替换第二个初始化器,

convenience init(name: String, age: Int) {
    self.init(name: name, age: age, nationality: "Canadian")
}

我可以以相同的方式创建相同的实例并获得相同的结果。那么区别是什么呢?我发现很难找到一个好的示例,其中便利初始化程序是完成创建类的特定实例的唯一方法,而第二个初始化程序无法像我在第一个示例中所做的那样完成。

4

1 回答 1

1

事实上,拥有两个初始化程序是 Bad Code™。正如 Leo Dabus 所说,使用最合适的 Swift 语言特性来表达该确切示例的方法是使用默认参数值。

class Person1 {
  var name: String
  var age: Int
  var nationality: String

  init(
    name: String,
    age: Int,
    nationality: String = "Canadian"
  ) {
    self.name = name
    self.age = age
    self.nationality = nationality
  }
}

便利初始化器是在存在另一个初始化器时使用的,它将为您节省代码重复。

例如,假设您添加了一个超类,并带有必需的 init:

class Person0 {
  var name: String
  var age: Int

  required init(
    name: String,
    age: Int
  ) {
    self.name = name
    self.age = age
  }
}

你不能只这样做;Swift 认为这不能满足要求:

class Person1: Person0 {
  var nationality: String

  required init(
    name: String,
    age: Int,
    nationality: String = "Canadian"
  ) {
    self.nationality = nationality
    super.init(
      name: name,
      age: age
    )
  }
}

相反,您可以复制,如下所示:

  required init(
    name: String,
    age: Int
  ) {
    nationality = "Canadian"
    super.init(
      name: name,
      age: age
    )
  }

  init(
    name: String,
    age: Int,
    nationality: String
  ) {
    self.nationality = nationality
    super.init(
      name: name,
      age: age
    )
  }

…或者使用一个指定的初始化器,它实际上完成了工作,并将所需的初始化器转发给它:

  required convenience init(
    name: String,
    age: Int
  ) {
    self.init(
      name: name,
      age: age,
      nationality: "Canadian"
    )
  }

  init(
    name: String,
    age: Int,
    nationality: String
  ) {
    self.nationality = nationality
    super.init(
      name: name,
      age: age
    )
  }

我发现很难找到一个好的示例,其中便利初始化程序是完成创建类的特定实例的唯一方法,而第二个初始化程序无法像我在第一个示例中所做的那样完成。

你总是可以复制代码。便利初始化器永远不会是唯一的方法。在最简单的情况下,就像我在这里展示的那样,每个初始值设定项只会有部分重复行。这没什么大不了的。但是重复数据删除会随着继承而堆积起来。

如果您的子类提供了其所有超类指定初始化器的实现(通过按照规则 1 继承它们,或者通过提供自定义实现作为其定义的一部分),那么它会自动继承所有超类便利初始化器。– https://docs.swift.org/swift-book/LanguageGuide/Initialization.html

因此,例如,当您使用便利 + 指定组合时,您可以跳过重写所需的 init。

例如

Person2(name: "Terrance Phillip", age: 42)

编译,从这里:

class Person2: Person1 {
  var flag: String

  override convenience init(
    name: String,
    age: Int,
    nationality: String
  ) {
    self.init(
      name: name,
      age: age,
      nationality: nationality,
      flag: ""
    )
  }

  init(
    name: String,
    age: Int,
    nationality: String,
    flag: String
  ) {
    self.flag = flag
    super.init(
      name: name,
      age: age,
      nationality: nationality
    )
  }
}

和这个人也是同一个人:

Person3()

…鉴于这种:

class Person3: Person2 {
  convenience init() {
    self.init(
      name: "Terrance Phillip",
      age: 42
    )
  }
}

作为指导,尽可能使用 1. 默认参数和 2. 便利初始化器。(并且作为 2 的扩展,在子类中将指定的初始化程序转换为方便的初始化程序。)您将需要维护更少的代码。

于 2020-03-11T04:53:22.387 回答