Swift - 初始化


类、结构体和枚举一旦在 Swift 4 中声明,就会被初始化以准备类的实例。存储属性的初始值被初始化,新实例的值也被初始化以进一步进行。创建初始化函数的关键字由“init()”方法执行。Swift 4 初始化器与 Objective-C 不同,它不返回任何值。它的功能是在处理之前检查新创建的实例的初始化情况。Swift 4 还提供了“取消初始化”过程,用于在实例被释放后执行内存管理操作。

存储属性的初始化器角色

存储属性必须在处理实例之前初始化其类和结构的实例。存储的属性使用初始值设定项来分配和初始化值,从而消除了调用属性观察器的需要。初始化器用于存储的属性

  • 创建初始值。

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

  • 要初始化特定数据类型的实例,需要使用“init()”。init() 函数内部没有传递任何参数。

句法

init() {
   //New Instance initialization goes here
}

例子

struct rectangle {
   var length: Double
   var breadth: Double
   init() {
      length = 6
      breadth = 12
   }
}

var area = rectangle()
print("area of rectangle is \(area.length*area.breadth)")

当我们使用 Playground 运行上述程序时,我们得到以下结果 -

area of rectangle is 72.0

这里,结构“矩形”使用成员长度和宽度作为“双”数据类型进行初始化。Init() 方法用于初始化新创建的成员 length 和 double 的值。通过调用矩形函数计算并返回矩形的面积。

默认设置属性值

Swift 4 语言提供了 Init() 函数来初始化存储的属性值。此外,用户可以在声明类或结构成员时默认初始化属性值。当属性在整个程序中单独采用相同的值时,我们可以单独在声明部分声明它,而不是在 init() 中初始化它。默认情况下设置属性值允许用户在为类或结构定义继承时启用。

struct rectangle {
   var length = 6
   var breadth = 12
}

var area = rectangle()
print("area of rectangle is \(area.length*area.breadth)")

当我们使用 Playground 运行上述程序时,我们得到以下结果 -

area of rectangle is 72

这里不是在 init() 中声明长度和宽度,而是在声明本身中初始化值。

参数初始化

在 Swift 4 语言中,用户可以使用 init() 将参数初始化为初始化器定义的一部分。

struct Rectangle {
   var length: Double
   var breadth: Double
   var area: Double
   
   init(fromLength length: Double, fromBreadth breadth: Double) {
      self.length = length
      self.breadth = breadth
      area = length * breadth
   }
   init(fromLeng leng: Double, fromBread bread: Double) {
      self.length = leng
      self.breadth = bread
      area = leng * bread
   }
}

let ar = Rectangle(fromLength: 6, fromBreadth: 12)
print("area is: \(ar.area)")

let are = Rectangle(fromLeng: 36, fromBread: 12)
print("area is: \(are.area)")

当我们使用 Playground 运行上述程序时,我们得到以下结果 -

area is: 72.0
area is: 432.0

本地和外部参数

初始化参数具有与函数和方法参数类似的局部和全局参数名称。局部参数声明用于在初始化主体内访问,外部参数声明用于调用初始化器。Swift 4 初始化器与函数和方法初始化器不同,它们不识别哪个初始化器用于调用哪个函数。

为了克服这个问题,Swift 4 为 init() 中的每个参数引入了一个自动外部名称。这个自动外部名称与每个初始化参数之前写入的本地名称等效。

struct Days {
   let sunday, monday, tuesday: Int
   init(sunday: Int, monday: Int, tuesday: Int) {
      self.sunday = sunday
      self.monday = monday
      self.tuesday = tuesday
   }
   init(daysofaweek: Int) {
      sunday = daysofaweek
      monday = daysofaweek
      tuesday = daysofaweek
   }
}

let week = Days(sunday: 1, monday: 2, tuesday: 3)
print("Days of a Week is: \(week.sunday)")
print("Days of a Week is: \(week.monday)")
print("Days of a Week is: \(week.tuesday)")

let weekdays = Days(daysofaweek: 4)
print("Days of a Week is: \(weekdays.sunday)")
print("Days of a Week is: \(weekdays.monday)")
print("Days of a Week is: \(weekdays.tuesday)")

当我们使用 Playground 运行上述程序时,我们得到以下结果 -

Days of a Week is: 1
Days of a Week is: 2
Days of a Week is: 3
Days of a Week is: 4
Days of a Week is: 4
Days of a Week is: 4

没有外部名称的参数

当初始化不需要外部名称时,下划线“_”用于覆盖默认Behave。

struct Rectangle {
   var length: Double
   
   init(frombreadth breadth: Double) {
      length = breadth * 10
   }
   init(frombre bre: Double) {
      length = bre * 30
   }
   init(_ area: Double) {
      length = area
   }
}

let rectarea = Rectangle(180.0)
print("area is: \(rectarea.length)")

let rearea = Rectangle(370.0)
print("area is: \(rearea.length)")

let recarea = Rectangle(110.0)
print("area is: \(recarea.length)")

当我们使用 Playground 运行上述程序时,我们得到以下结果 -

area is: 180.0
area is: 370.0
area is: 110.0

可选的财产类型

当某个实例中存储的属性不返回任何值时,该属性被声明为“可选”类型,指示该特定类型不返回“无值”。当存储的属性被声明为“可选”时,它会在初始化期间自动将值初始化为“nil”。

struct Rectangle {
   var length: Double?
   
   init(frombreadth breadth: Double) {
      length = breadth * 10
   }
   init(frombre bre: Double) {
      length = bre * 30
   }
   init(_ area: Double) {
      length = area
   }
}

let rectarea = Rectangle(180.0)
print("area is: \(rectarea.length)")

let rearea = Rectangle(370.0)
print("area is: \(rearea.length)")

let recarea = Rectangle(110.0)
print("area is: \(recarea.length)")

当我们使用 Playground 运行上述程序时,我们得到以下结果 -

area is: Optional(180.0)
area is: Optional(370.0)
area is: Optional(110.0)

在初始化期间修改常量属性

初始化还允许用户修改常量属性的值。在初始化期间,类属性允许其类实例由超类修改,而不是由子类修改。例如,在前面的程序中,“长度”在主类中被声明为“变量”。下面的程序变量“长度”被修改为“常量”变量。

struct Rectangle {
   let length: Double?
   
   init(frombreadth breadth: Double) {
      length = breadth * 10
   }
   init(frombre bre: Double) {
      length = bre * 30
   }
   init(_ area: Double) {
      length = area
   }
}

let rectarea = Rectangle(180.0)
print("area is: \(rectarea.length)")

let rearea = Rectangle(370.0)
print("area is: \(rearea.length)")

let recarea = Rectangle(110.0)
print("area is: \(recarea.length)")

当我们使用 Playground 运行上述程序时,我们得到以下结果 -

area is: Optional(180.0)
area is: Optional(370.0)
area is: Optional(110.0)

默认初始化器

默认初始值设定项为具有默认值的基类或结构的所有声明属性提供新实例。

class defaultexample {
   var studname: String?
   var stmark = 98
   var pass = true
}
var result = defaultexample()

print("result is: \(result.studname)")
print("result is: \(result.stmark)")
print("result is: \(result.pass)")

当我们使用 Playground 运行上面的程序时,我们得到以下结果。-

result is: nil
result is: 98
result is: true

上面的程序定义的类名为“defaultexample”。三个成员函数默认初始化为“studname?” 要存储“nil”值,“stmark”为 98,“pass”为布尔值“true”。同样,在处理类成员类型之前,可以将类中的成员值初始化为默认值。

结构类型的成员初始化器

当用户未提供自定义初始化程序时,Swift 4 中的结构类型将自动接收“成员初始化程序”。它的主要功能是使用默认的成员初始化来初始化新的结构体实例,然后将新实例的属性按名称传递给成员初始化。

struct Rectangle {
   var length = 100.0, breadth = 200.0
}
let area = Rectangle(length: 24.0, breadth: 32.0)

print("Area of rectangle is: \(area.length)")
print("Area of rectangle is: \(area.breadth)")

当我们使用 Playground 运行上述程序时,我们得到以下结果 -

Area of rectangle is: 24.0
Area of rectangle is: 32.0

在初始化期间,结构体的成员函数默认初始化为“长度”为“100.0”,“宽度”为“200.0”。但在处理变量长度和宽度时,这些值会被覆盖为 24.0 和 32.0。

值类型的初始化器委托

初始化器委托被定义为从其他初始化器调用初始化器。它的主要功能是充当可重用性,以避免多个初始化程序之间的代码重复。

struct Stmark {
   var mark1 = 0.0, mark2 = 0.0
}
struct stdb {
   var m1 = 0.0, m2 = 0.0
}

struct block {
   var average = stdb()
   var result = Stmark()
   init() {}
   init(average: stdb, result: Stmark) {
      self.average = average
      self.result = result
   }

   init(avg: stdb, result: Stmark) {
      let tot = avg.m1 - (result.mark1 / 2)
      let tot1 = avg.m2 - (result.mark2 / 2)
      self.init(average: stdb(m1: tot, m2: tot1), result: result)
   }
}

let set1 = block()
print("student result is: \(set1.average.m1, set1.average.m2)
\(set1.result.mark1, set1.result.mark2)")

let set2 = block(average: stdb(m1: 2.0, m2: 2.0),
result: Stmark(mark1: 5.0, mark2: 5.0))
print("student result is: \(set2.average.m1, set2.average.m2)
\(set2.result.mark1, set2.result.mark2)")

let set3 = block(avg: stdb(m1: 4.0, m2: 4.0),
result: Stmark(mark1: 3.0, mark2: 3.0))
print("student result is: \(set3.average.m1, set3.average.m2)
\(set3.result.mark1, set3.result.mark2)")

当我们使用 Playground 运行上述程序时,我们得到以下结果 -

(0.0,0.0) (0.0,0.0)
(2.0,2.0) 5.0,5.0)
(2.5,2.5) (3.0,3.0)

初始化器委托规则

值类型 班级类型
结构和枚举等值类型不支持继承。引用其他初始化器是通过 self.init 完成的 支持继承。检查所有存储的属性值是否已初始化

类继承和初始化

类类型有两种初始化器来检查定义的存储属性是否接收初始值,即指定初始化器和便利初始化器。

指定初始化器和便利初始化器

指定初始化器 便利初始化程序
被视为类的主要初始化 被视为支持类的初始化
所有类属性均已初始化,并调用适当的超类初始值设定项以进行进一步初始化 使用便利初始化程序调用指定的初始化程序,为特定用例或输入值类型创建类实例
为每个类至少定义一个指定的初始值设定项 当类不需要初始化器时,不需要强制定义便利初始化器。
Init(参数) { 语句 } 方便 init(参数) { 语句 }

指定初始化程序

class mainClass {
   var no1 : Int // local storage
   init(no1 : Int) {
      self.no1 = no1 // initialization
   }
}

class subClass : mainClass {
   var no2 : Int // new subclass storage
   init(no1 : Int, no2 : Int) {
      self.no2 = no2 // initialization
      super.init(no1:no1) // redirect to superclass
   }
}

let res = mainClass(no1: 10)
let print = subClass(no1: 10, no2: 20)

print("res is: \(res.no1)")
print("res is: \(print.no1)")
print("res is: \(print.no2)")

当我们使用 Playground 运行上述程序时,我们得到以下结果 -

res is: 10
res is: 10
res is: 20

便捷初始化程序

class mainClass {
   var no1 : Int // local storage
   init(no1 : Int) {
      self.no1 = no1 // initialization
   }
}

class subClass : mainClass {
   var no2 : Int
   init(no1 : Int, no2 : Int) {
      self.no2 = no2
      super.init(no1:no1)
   }
   // Requires only one parameter for convenient method
   override convenience init(no1: Int) {
      self.init(no1:no1, no2:0)
   }
}

let res = mainClass(no1: 20)
let print = subClass(no1: 30, no2: 50)

print("res is: \(res.no1)")
print("res is: \(print.no1)")
print("res is: \(print.no2)")

当我们使用 Playground 运行上述程序时,我们得到以下结果 -

res is: 20
res is: 30
res is: 50

初始化器继承和重写

默认情况下,Swift 4 不允许其子类为其成员类型继承其超类初始值设定项。继承仅在某种程度上适用于超类初始值设定项,这将在自动初始值设定项继承中讨论。

当用户需要在超类中定义初始值设定项时,具有初始值设定项的子类必须由用户定义为自定义实现。当子类必须对超类进行重写时,必须声明“override”关键字。

class sides {
   var corners = 4
   var description: String {
      return "\(corners) sides"
   }
}

let rectangle = sides()
print("Rectangle: \(rectangle.description)")

class pentagon: sides {
   override init() {
      super.init()
      corners = 5
   }
}

let bicycle = pentagon()
print("Pentagon: \(bicycle.description)")

当我们使用 Playground 运行上述程序时,我们得到以下结果 -

Rectangle: 4 sides
Pentagon: 5 sides

指定初始化器和便利初始化器的实际应用

class Planet {
   var name: String
   init(name: String) {
      self.name = name
   }
   convenience init() {
      self.init(name: "[No Planets]")
   }
}

let plName = Planet(name: "Mercury")
print("Planet name is: \(plName.name)")

let noplName = Planet()
print("No Planets like that: \(noplName.name)")

class planets: Planet {
   var count: Int
   init(name: String, count: Int) {
      self.count = count
      super.init(name: name)
   }
   override convenience init(name: String) {
      self.init(name: name, count: 1)
   }
}

当我们使用 Playground 运行上述程序时,我们得到以下结果 -

Planet name is: Mercury
No Planets like that: [No Planets]

失败的初始化程序

当定义类、结构或枚举值时出现任何初始化程序失败时,必须通知用户。变量的初始化有时会因为以下原因而失败:

  • 参数值无效。
  • 缺乏所需的外部来源。
  • 阻止初始化成功的条件。

为了捕获初始化方法引发的异常,Swift 4 生成了一个名为“失败初始化器”的灵活初始化,以通知用户在初始化结构、类或枚举成员时有一些未被注意的情况。捕获可失败初始化程序的关键字是“init?”。此外,不能使用相同的参数类型和名称来定义可失败和不可失败的初始值设定项。

struct studrecord {
   let stname: String
   init?(stname: String) {
      if stname.isEmpty {return nil }
      self.stname = stname
   }
}
let stmark = studrecord(stname: "Swing")

if let name = stmark {
   print("Student name is specified")
}
let blankname = studrecord(stname: "")

if blankname == nil {
   print("Student name is left blank")
}

当我们使用 Playground 运行上述程序时,我们得到以下结果 -

Student name is specified
Student name is left blank

枚举的失败初始化器

Swift 4 语言还提供了灵活性,可以为枚举提供 Failable 初始值设定项,以便在枚举成员未初始化值时通知用户。

enum functions {
   case a, b, c, d
   init?(funct: String) {
      switch funct {
      case "one":
         self = .a
      case "two":
         self = .b
      case "three":
         self = .c
      case "four":
         self = .d
      default:
         return nil
      }
   }
}
let result = functions(funct: "two")

if result != nil {
   print("With In Block Two")
}
let badresult = functions(funct: "five")

if badresult == nil {
   print("Block Does Not Exist")
}

当我们使用 Playground 运行上述程序时,我们得到以下结果 -

With In Block Two
Block Does Not Exist

类的失败初始化器

当使用枚举和结构声明时,可失败的初始化程序会在其实现中的任何情况下发出初始化失败的警报。但是,类中的可失败初始化程序仅在存储的属性设置为初始值后才会发出失败警报。

class studrecord {
   let studname: String!
   init?(studname: String) {
      self.studname = studname
      if studname.isEmpty { return nil }
   }
}

if let stname = studrecord(studname: "Failable Initializers") {
   print("Module is \(stname.studname)")
}

当我们使用 Playground 运行上述程序时,我们得到以下结果 -

Module is Optional("Failable Initializers")

覆盖失败的初始化程序

与初始化一样,用户还可以覆盖子类内的超类可失败初始化程序。超类可失败初始化也可以在子类不可失败初始化器中重写。

当用不可失败的子类初始化重写可失败的超类初始化器时,子类初始化器无法委托给超类初始化器。

不可失败的初始化程序永远不能委托给可失败的初始化程序。

下面给出的程序描述了可失败和不可失败的初始化程序。

class Planet {
   var name: String
   
   init(name: String) {
      self.name = name
   }
   convenience init() {
      self.init(name: "[No Planets]")
   }
}
let plName = Planet(name: "Mercury")
print("Planet name is: \(plName.name)")

let noplName = Planet()
print("No Planets like that: \(noplName.name)")
   
class planets: Planet {
   var count: Int
   
   init(name: String, count: Int) {
      self.count = count
      super.init(name: name)
   }
   override convenience init(name: String) {
      self.init(name: name, count: 1)
   }
}

当我们使用 Playground 运行上述程序时,我们得到以下结果 -

Planet name is: Mercury
No Planets like that: [No Planets]

初始化!失败的初始化程序

Swift 4 提供了“init?” 定义一个可选的实例可失败初始化程序。定义特定类型“init!”的隐式解包可选实例 已指定。

struct studrecord {
let stname: String

   init!(stname: String) {
      if stname.isEmpty {return nil }
      self.stname = stname
   }
}
let stmark = studrecord(stname: "Swing")

if let name = stmark {
   print("Student name is specified")
}

let blankname = studrecord(stname: "")

if blankname == nil {
   print("Student name is left blank")
}

当我们使用 Playground 运行上述程序时,我们得到以下结果 -

Student name is specified
Student name is left blank

所需的初始化程序

要声明初始化的每个子类,需要在 init() 函数之前定义“required”关键字。

class classA {
   required init() {
      var a = 10
      print(a)
   }
}

class classB: classA {
   required init() {
      var b = 30
      print(b)
   }
}

let res = classA()
let print = classB()

当我们使用 Playground 运行上述程序时,我们得到以下结果 -

10
30
10