Swift init()

时间:2020-02-23 14:44:02  来源:igfitidea点击:

在本Swift教程中,我们将讨论一个重要的概念,即Swift初始化或者Swift初始化。
当我们创建某种类型的实例时,就会发生初始化。

Swift init()

初始化是准备使用的类,结构或者枚举实例的过程。
此过程涉及为该实例上的每个存储属性设置一个初始值,并执行新实例准备使用之前所需的任何其他设置或者初始化。

初始化程序类似于Java编程中的构造函数。
Swift是一种类型安全的语言,它为初始化程序设置了很多规则。
除非您很好地理解这个概念,否则实施起来可能会很棘手。

Swift init()语法

init() {
  //initialise the stored properties here.
}

让我们在下面看一个示例类。

class A{
  
  //Compilation error. No initializer is defined.
  var a : Int
  var b : String
  var c : Int?
  let website = "theitroad"
}

上等程序无法编译。
迅速的编译器抱怨存储的属性未初始化。
存储的属性不能保持不确定状态。

这给我们提供了两种可能的选择:

  • 在属性定义本身中分配默认属性值。

  • 使用初始化程序" init()"来初始化属性。

让我们一次看看每种方法。

class A{
  
  var a : Int = 5
  var b : String = "Hello. How you're doing"
  var c : Int?
  let website = "theitroad"
}

其中我们为每个存储的属性都设置了默认值,因此Swift会为我们隐式提供默认的初始化程序。
初始化类后,就可以在类的实例上使用点运算符访问所有属性和函数。

var object = A()
object.a = 10
object.c = 2

第二种方法是使用" init()"方法初始化存储的属性,如下所示。

class A{
  
  var a : Int
  var b : String
  var c : Int?
  let website = "theitroad"
  
  init(a: Int, b: String) {
      self.a = a
      self.b = b
  }
}

var object = A(a: 5, b: "Hello World")

注意:Swift Optional不是存储的属性。
因此,它们不需要初始化。

使用self属性可以在init()方法内部访问存储的属性。

注意:self用于在其自己的实例方法中引用当前实例(类似于Java中的this)。
上面的初始化器是该类的主要初始化器。
也称为指定的初始化程序(我们将在后面讨论)。

通过初始化程序,我们也可以修改常量属性。

class A{
  
  var a : Int
  var b : String
  var c : Int?
  let website : String
  
  init(a: Int, b: String, website: String) {
      self.a = a
      self.b = b
      self.website = website
  }
}

var object = A(a: 5,b: "Hello World", website: "theitroad")

结构的成员初始化器

结构是值类型,不一定需要定义初始化程序。
除非您定义了自定义初始化程序,否则结构类型会自动收到一个成员初始化程序。

以下是代码片段,描述了初始化结构的各种方法。

struct Rect{
  var length : Int
  var breadth : Int
}
var r = Rect(length: 5, breadth: 10)
struct Rect{
  var length : Int = 5
  var breadth : Int = 10
}

var r = Rect()
var r1 = Rect(length: 10, breadth: 5)

由于我们已在上面的代码段中为存储的属性分配了默认值,因此我们会收到一个默认的初始化程序,其中没有成员初始化以及成员初始化程序。

struct Rect{
  var length : Int
  var breadth : Int
  
  init(length: Int, breadth: Int) {
      self.length =  length + 10
      self.breadth = breadth + 10
  }
}
var r = Rect(length: 10, breadth: 5)

在上述情况下,我们定义了自己的自定义初始化程序。

使用不带外部名称的参数当初始化器不需要外部名称时,下划线" _"用于表示相同的名称,如下所示。

class A{
  
  var a : Int
  var b : String
  var c : Int?
  let website = "theitroad"
  
  init(_ a: Int, _ b: String) {
      self.a = a
      self.b = b
  }
}

var object = A(5,"Hello World")
struct Rect{
  var length : Int
  var breadth : Int
  
  init(_ length: Int, _ breadth: Int) {
      self.length =  length + 10
      self.breadth = breadth + 10
  }
}
var r = Rect(10, 10)

Swift初始化程序的类型

类的初始化程序可以大致分为以下类型:

  • 指定的初始化程序:这是该类的主要初始化程序。
    在调用任何超类初始化程序之前,它必须完全初始化其类引入的所有属性。
    一个类可以具有多个指定的初始化程序。
    每个类必须至少有一个指定的初始化程序。

  • 便利的初始化程序:这些是辅助的,支持类的初始化程序。
    他们必须调用相同类的指定初始化器。
    这些是可选的,可用于自定义设置。
    它们以相同的样式编写,但在init关键字之前放置了" convenience`修饰符

class Student{
  
  var name : String
  var degree : String
  
  init(name : String, degree: String) {
      self.name = name
      self.degree = degree
  }
  
  convenience init()
  {
      self.init(name: "Unnamed", degree: "Computer Science")
  }
  
}
var student = Student()
student.degree //"Computer Science"
student.name //"Unnamed"

将默认值分配给存储的属性时,便捷初始化器很有用。

值类型的Swift初始化程序委托

可以从另一个调用初始化程序,从而避免代码重复。
像结构这样的值类型不支持继承。
因此,唯一可能的方法是在同一结构内调用初始化程序。
下面给出一个例子。

struct Rect{
  var length : Int
  var breadth : Int
  
  init(_ length: Int, _ breadth: Int) {
      self.length =  length
      self.breadth = breadth
  }
  
  init(_ length: Int)
  {
      self.init(length, length)
  }
}
var r = Rect(10, 5)
var r1 = Rect(15) //initialises the length and breadth to 15

引用类型的Swift初始化程序委托

作为引用类型的类支持继承。
因此,初始化器也可以从超类调用其他初始化器,从而增加了正确继承和初始化所有值的责任。
以下是定义用于处理初始化程序之间关系的主要规则。

  • 指定的初始值设定项必须从其直接超类调用指定的初始值设定项。

  • 便利初始化程序必须从同一类调用另一个初始化程序。

  • 便利初始化程序必须最终调用指定的初始化程序。

下图描述了上述规则。
资料来源:Apple Docs

指定的初始化程序必须始终委托。
便捷初始化程序必须始终委派。

对于子类中的便捷初始化程序,无法使用super关键字。

Swift初始化程序的继承和覆盖

除非满足某些条件(自动初始化程序继承),否则默认情况下,Swift中的子类不会继承其超类的初始化程序。
这样做是为了防止子类中的半初始化。
让我们看看指定初始化和便捷初始化如何通过继承工作。
我们将定义一个由相关子类继承的Vehicle基础类。
在程序中,我们将枚举用作类型。

我们的基类Vehicle定义如下。

enum VehicleType : String {
  case twoWheeler = "TwoWheeler"
  case fourWheeler = "FourWheeler"
}

class Vehicle{
  
  var vehicleType : VehicleType
  
  init(vehicleType: VehicleType) {
      self.vehicleType = vehicleType
      print("Class Vehicle. vehicleType is \(self.vehicleType.rawValue)\n")
  }
  
  convenience init()
  {
      self.init(vehicleType: .fourWheeler)
  }
}

var v = Vehicle(vehicleType: .twoWheeler)

注意:便利初始化程序必须使用self.init调用同一类的指定初始化程序。

让我们定义上述类别的子类别,如下所示。

enum TwoWheelerType : String
{
  case scooty = "Scooty"
  case bike = "Bike"
}

class TwoWheeler : Vehicle{
  
  var twoWheelerType : TwoWheelerType
  var manufacturer : String
  
  init(twoWheelerType : TwoWheelerType, manufacturer : String, vType : VehicleType) {
      self.twoWheelerType = twoWheelerType
      self.manufacturer = manufacturer
      print("Class TwoWheeler. \(self.twoWheelerType.rawValue) manufacturer is \(self.manufacturer)")
      super.init(vehicleType: vType)
      
  }
}

注意事项:

  • 子类的指定初始化器必须在调用超类的指定初始化器之前初始化其自身的属性。

  • 只有在调用super.init之后,子类才能修改超类的继承属性。

以下代码将导致编译时错误。

class TwoWheeler : Vehicle{
  
  var twoWheelerType : TwoWheelerType
  var manufacturer : String
  
  init(twoWheelerType : TwoWheelerType, manufacturer : String, vType : VehicleType) {
      self.twoWheelerType = twoWheelerType
      self.manufacturer = manufacturer
      self.vehicleType = vType //Won't compile
      super.init(vehicleType: vType)
      //self.vehicleType = .fourWheeler //This would work.
      
  }
}

var t = TwoWheeler(twoWheelerType: .scooty, manufacturer: "Hero Honda", vType: .twoWheeler)

如前所述,父类不会自动继承父类的初始值设定项。
因此,以下初始化将失败。

var t = TwoWheeler(vehicleType: .twoWheeler) //manufacturer property isn't initialized.

要覆盖初始化程序,子类初始化程序必须与超类的指定初始化程序匹配。
在这种情况下,override关键字将附加到初始化程序。

class TwoWheeler : Vehicle{
  
  var twoWheelerType : TwoWheelerType
  var manufacturer : String
  
  init(twoWheelerType : TwoWheelerType, manufacturer : String, vType : VehicleType) {
      self.twoWheelerType = twoWheelerType
      self.manufacturer = manufacturer
      print("Class TwoWheeler. \(self.twoWheelerType.rawValue) manufacturer is \(self.manufacturer)")
      super.init(vehicleType: vType)
      
  }
  
  override init(vehicleType: VehicleType)
  {
      print("Class TwoWheeler. Overriden Initializer. \(vehicleType.rawValue)")
      self.twoWheelerType = .bike
      self.manufacturer = "Not defined"
      super.init(vehicleType: vehicleType)
  }

由于参数名称不同,因此以下初始化程序不会覆盖超类中的一个。

//This would give a compile-time error since the parameter v doesn't match with the superclass.
override init(v: VehicleType)
  {
      self.twoWheelerType = .bike
      self.manufacturer = "Not defined"
      super.init(vehicleType: v)
  }

使用便捷初始化程序覆盖超类中的一个。

class TwoWheeler : Vehicle{
  
  var twoWheelerType : TwoWheelerType
  var manufacturer : String
  
  init(twoWheelerType : TwoWheelerType, manufacturer : String, vType : VehicleType) {
      self.twoWheelerType = twoWheelerType
      self.manufacturer = manufacturer
      print("Class TwoWheeler. \(self.twoWheelerType.rawValue) manufacturer is \(self.manufacturer)")
      super.init(vehicleType: vType)
      
  }
  
  override convenience init(vehicleType: VehicleType) {
      self.init(twoWheelerType: .bike, manufacturer: "Not Defined", vType: .twoWheeler)
      self.vehicleType = vehicleType
  }
}
var t = TwoWheeler(twoWheelerType: .scooty, manufacturer: "Hero Honda", vType: .twoWheeler)
t = TwoWheeler(vehicleType: .twoWheeler)

//Output
Following gets printed on the console:
Class TwoWheeler. Scooty manufacturer is Hero Honda
Class Vehicle. vehicleType is TwoWheeler

Class TwoWheeler. Bike manufacturer is Not Defined
Class Vehicle. vehicleType is TwoWheeler

便利初始化程序具有附加的" override"关键字。
它调用相同类别的指定的初始化器。
注意:关键字"便捷"和"覆盖"的顺序无关紧要。

必需的初始化器

在初始化器之前编写关键字" required"表示每个子类必须实现该初始化器。
同样," required"修饰符也必须出现在相应的子类实现中。
下面给出了以上两个类的Required Initializers的示例。

class Vehicle{
  
  var vehicleType : VehicleType
  
  required init(vehicleType: VehicleType) {
      self.vehicleType = vehicleType
      print("Class Vehicle. vehicleType is \(self.vehicleType.rawValue)\n")
  }
  
  convenience init()
  {
      self.init(vehicleType: .fourWheeler)
  }
}

class TwoWheeler : Vehicle{
  
  var twoWheelerType : TwoWheelerType
  var manufacturer : String
  
  init(twoWheelerType : TwoWheelerType, manufacturer : String, vType : VehicleType) {
      self.twoWheelerType = twoWheelerType
      self.manufacturer = manufacturer
      print("Class TwoWheeler. \(self.twoWheelerType.rawValue) manufacturer is \(self.manufacturer)")
      super.init(vehicleType: vType)
      
  }
  
   required init(vehicleType: VehicleType) {
      self.manufacturer = "Not Defined"
      self.twoWheelerType = .bike
      super.init(vehicleType: vehicleType)
  }
}

注意:添加必需的修饰符,表示将覆盖初始化程序。
因此,在上述情况下可以忽略override关键字。

在便利性中使用必需的初始化器必需和便利性初始化器彼此独立,可以一起使用。
让我们创建"车辆"的另一个子类,以演示"必需"和"便捷"修饰符的结合使用。

enum FourWheelerType : String
{
  case car = "Car"
  case bus = "Bus"
  case truck = "Truck"
}

class FourWheeler : Vehicle
{
  var fourWheelerType : FourWheelerType
  var name : String
  
  init(fourWheelerType : FourWheelerType, name: String, vehicleType: VehicleType) {
      self.fourWheelerType = fourWheelerType
      self.name = name
      print("Class FourWheeler. \(self.fourWheelerType.rawValue) Model is \(self.name)")
      super.init(vehicleType: vehicleType)
      self.vehicleType = vehicleType
  }
  
  required convenience init(vehicleType: VehicleType) {
      self.init(fourWheelerType: .bus, name: "Mercedes", vehicleType: vehicleType)
  }
}

class Car : FourWheeler{
  
  var model : String
  
  init(model: String) {
      self.model = model
      print("Class Car. Model is \(self.model)")
      super.init(fourWheelerType: .car, name: self.model, vehicleType: .fourWheeler)
  }
  
  required init(vehicleType: VehicleType)
  {
      self.model = "Not defined"
      print("Class Car. Model is \(self.model)")
      super.init(fourWheelerType: .car, name: self.model, vehicleType: vehicleType)
  }
  
}

上面的代码片段中要注意的重要事项:

  • 便利初始化器是类中的辅助初始化器。

  • 根据需要设置便捷初始化器意味着必须在子类中实现它。

自动初始化程序继承

在两种情况下,子类会自动从超类继承初始化程序。

  • 不要在子类中定义任何指定的初始值设定项。

  • 实现超类的所有指定初始化器。
    所有便利初始化程序也将自动继承。

下面的代码片段演示了第一个有效的规则:

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

class Tutorial: Name {
  
  var tutorial : String? = "Swift Initialization"
}

var parentObject = Name(n: "Anupam")
var childObject = Tutorial(n: "theitroad")

下面的代码片段演示了第二条有效规则。

class Name {
  
  var name: String
  
  init(n: String) {
      self.name = n
  }
  
  convenience init()
  {
      self.init(n: "No name assigned")
  }
}

class Tutorial: Name {
  
  var tutorial : String? = "Swift Tutorial"
  
  override init(n : String) {
      super.init(n: n)
  }
}

var parentObject = Name(n: "Anupam")
var childObject = Tutorial(n: "theitroad")
var childObject2 = Tutorial()
print(childObject2.name) //prints "No name assigned

上面代码的子类中自动提供了超类的便捷初始化器。

Swift失败的初始化程序

我们可以在类,结构或者枚举上使用关键字" init?"定义一个失败的初始化器,该初始化器将在初始化过程失败时触发。
初始化可能由于各种原因而失败:无效的参数值,缺少外部源等。
失败的初始化程序会创建其初始化类型的可选值。
我们将返回nil来触发初始化失败(尽管" init"不会返回任何内容)。
具有结构的失败初始化器

struct SName {
  let name: String
  init?(name: String) {
      if name.isEmpty { return nil }
      self.name = name
  }
}

var name = SName(name: "theitroad")
if name != nil {
  print("init success") //this gets displayed
}
else{
  print("init failed")
}
name  = SName(name: "")

if name != nil {
  print("init success")
}
else{
  print("init failed") //this gets displayed
}

枚举失败的初始化程序

enum CharacterExists {
  case A, B
  init?(symbol: Character) {
      switch symbol {
      case "A":
          self = .A
      case "B":
          self = .B
      default:
          return nil
      }
  }
}

let ch = CharacterExists(symbol: "C")
if ch != nil {
  print("Init failed. Character doesn't exist")
}
class CName {
  let name: String
  init?(name: String) {
      if name.isEmpty { return nil }
      self.name = name
  }
}
var name  = CName(name: "")

if name != nil {
  print("init success")
}
else{
  print("init failed")
}

注意:失败的初始值设定项和失败的初始值设定项不能具有相同的参数类型和名称。

覆盖失败的初始化程序

您可以在子类中覆盖失败的初始化程序。
一个失败的初始化器可以用一个不失败的初始化器来覆盖,但是反之亦然。
下面给出了使用非失败的初始化器覆盖失败的示例。

class CName {
  let name: String
  init?(name: String) {
      if name.isEmpty { return nil }
      self.name = name
  }
}
var name  = CName(name: "")

class SubName : CName{
  
  var age : Int
  override init(name: String)
  {
      self.age = 23
      super.init(name: name)!  
  }
}

注意:强制展开用于从超类调用可失败的初始化器,作为子类不可失败的初始化器实现的一部分。