Swift建構函式(Initializer)


在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

這裡結構體rectangle初始化成員:lengthbreadthDouble資料型別。 Init()方法用於初始化新建立的成員:lengthbreadthdouble值。 通過呼叫rectangle函式計算並返回矩形面積。

預設設定屬性值

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

無外部名稱的引數

如果初始化下劃線不需要外部名稱,則使用_覆蓋預設行為。

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

optional屬性型別

當某個範例的儲存屬性沒有返回任何值時,該屬性被宣告為optional型別,表明該特定型別返回’無值’。 當儲存屬性宣告為optional時,它會在初始化過程中自動將值初始化為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)

初始化期間修改常數屬性

初始化還允許使用者修改常數屬性的值。 在初始化期間,類屬性允許類範例由超類而不是子類修改。 例如,在前一個程式中考慮length在主類中宣告為’變數’。 以下程式變數length被修改為’常數’。

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值,stmark98pass為布林值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

在初始化期間,結構體初始化為其成員函式,初始化成員length的值為100.0breadth的值為200.0。 但是在處理變數lengthbreadth期間,覆蓋新值為24.032.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(parameters) { statements } convenience init(parameters) { statements }

指定初始化程式程式碼

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不允許子類為成員型別繼承超類初始值設定項。 繼承僅在某種程度上適用於超類初始值設定項,這將在自動初始化程式繼承中討論。

當使用者需要在超類中定義初始化器時,具有初始化器的子類必須由使用者定義為自定義實現。 當子類必須重寫超類覆蓋時,必須宣告關鍵字。

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生成一個名為failable initializer的初始化器,以通知使用者在初始化結構,類或列舉成員時不會注意到某些內容。 捕獲可用初始化程式的關鍵字是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]

init!可失敗初始化程式

Swift 4提供init? 定義一個可選的範例failable初始化程式。 定義特定型別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