Swift中的init方法

摘要:Swift有着超級嚴格的初始化方法,不只強化了designated初始化方法的地位,全部不加修飾的init方法都須要在方法中確保非Optional的實例變量被賦值初始化,而在子類中,也強制調用super版本的designated初始化。安全

咱們在深刻初始化方法以前,不妨先再想一想Swift中的初始化想要達到一種怎樣的目的。app

其實就是安全。在Objective-C中,init方法是很是不安全的:沒有人能保證init只被調用一次,也沒有人保證在初始化方法調用之後,實例的各個變量都完成初始化,甚至若是在初始化裏使用屬性進行設置的話,還可能會形成各類問題。雖然Apple也明確說明了不該該在init中使用屬性來訪問,但這並非編譯器強制的,所以仍是會有不少開發者犯這樣的錯誤。ide

因此Swift有了超級嚴格的初始化方法。一方面,Swift強化了designated初始化方法的地位。Swift中不加修飾的init方法都須要在方法中保證全部非Optional的實例變量被賦值初始化,而在子類中也強制 (顯式或隱式地)調用super版本的designated初始化,因此不管如何走何種路徑,被初始化的對象老是能夠完成完整的初始化的。oop

 

[cpp]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. class ClassA {  
  2.     let numA: Int  
  3.     init(num: Int) {  
  4.         numA = num  
  5.     }  
  6. }  
  7. class ClassB: ClassA {  
  8.     let numB: Int  
  9.   
  10.     override init(num: Int) {  
  11.         numB = num + 1  
  12.         super.init(num: num)  
  13.     }  
  14. }  

 

在上面的示例代碼中,注意在init裏咱們能夠對let的實例常量進行賦值,這是初始化方法的重要特色。在Swift中let聲明的值是不變量,沒法被寫入賦值,這對於構建線程安全的API十分有用。而由於Swift的init只可能被調用一次,所以在init中咱們能夠爲不變量進行賦值,而不會引發任何線程安全的問題。ui

與designated初始化方法對應的是在init前加上convenience關鍵字的初始化方法。這類方法是Swift初始化方法中的「二等公民」,只做爲補充和提供使用上的方便。全部的convenience初始化方法都必須調用同一個類中的designated初始化完成設置,另外convenience的初始化方法是不能被子類重寫或從子類中以super的方式被調用的。spa

 

[cpp]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. class ClassA {  
  2.     let numA: Int  
  3.     init(num: Int) {  
  4.         numA = num  
  5.     }  
  6.     convenience init(bigNum: Bool) {  
  7.         self.init(num: bigNum ? 10000 : 1)  
  8.     }  
  9. }  
  10. class ClassB: ClassA {  
  11.     let numB: Int  
  12.     override init(num: Int) {  
  13.         numB = num + 1  
  14.         super.init(num: num)  
  15.     }  
  16. }  

 

只要在子類中實現重寫了父類convenience方法所須要的init方法的話,咱們在子類中就也可使用父類的convenience初始化方法了。好比在上面的代碼中,咱們在ClassB裏實現了init(num: Int)的重寫。這樣,即便在ClassB中沒有bigNum版本的convenience init(bigNum: Bool),咱們仍然仍是能夠用這個方法來完成子類初始化:.net

 

[cpp]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. let anObj = ClassB(bigNum: true)  
  2. // anObj.numA = 10000, anObj.numB = 10001  

 

所以進行一下總結,能夠看到初始化方法永遠遵循如下兩個原則:線程

 

  1. 初始化路徑必須保證對象徹底初始化,這能夠經過調用本類型的designated初始化方法來獲得保證;
  2. 子類的designated初始化方法必須調用父類的designated方法,以保證父類也完成初始化。

 

對於某些咱們但願子類中必定實現的designated初始化方法,咱們能夠經過添加required關鍵字進行限制,強制子類對這個方法重寫實現。這樣的一個最大的好處是能夠保證依賴於某個designated初始化方法的convenience一直能夠被使用。一個現成的例子就是上面的init(bigNum: Bool):若是咱們但願這個初始化方法對於子類必定可用,那麼應當將init(num: Int)聲明爲必須,這樣咱們在子類中調用init(bigNum: Bool)時就始終可以找到一條徹底初始化的路徑了:code

 

[cpp]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. class ClassA {  
  2.     let numA: Int  
  3.     required init(num: Int) {  
  4.         numA = num  
  5.     }  
  6.     convenience init(bigNum: Bool) {  
  7.         self.init(num: bigNum ? 10000 : 1)  
  8.     }  
  9. }  
  10. class ClassB: ClassA {  
  11.     let numB: Int  
  12.     required init(num: Int) {  
  13.         numB = num + 1  
  14.         super.init(num: num)  
  15.     }  
  16. }  

 

另外須要說明的是,其實不只僅是對designated初始化方法,對於convenience的初始化方法,咱們也能夠加上required以確保子類對其進行實現。這在要求子類不直接使用父類中的convenience初始化方法時會很是有幫助。對象

相關文章
相關標籤/搜索