Swift - 了解一下Swift初始化

前言:

本篇文章色瘩,主要是針對Swift初始化的印象加深伪窖。搜集了表達比較好的初始化文章,進行了匯總居兆。

首先我們從初始化方法順序開始覆山,了解初始化方法有三種,指定史辙、便利汹买、和必要初始化方法佩伤。從而針對自定義控件,初始化進行驗證晦毙,得出自定義控件重寫父類初始化方法時生巡,必須提供必要初始化方法,否則會報錯见妒。而后又提供了孤荣,為什么Swift:必須添加init?(coder decoder: NSCoder)的原因,加深理解须揣。

目錄:
001 - 初始化方法順序
002 - 了解一下指定盐股、便利、必要初始化
003 - Swift:為什么必須添加init?(coder decoder: NSCoder)的原因

初始化方法順序

與 Objective-C 不同耻卡,Swift 的初始化方法需要保證類型的所有屬性都被初始化疯汁。所以初始化方法的調(diào)用順序就很有講究。在某個類的子類中卵酪,初始化方法里語句的順序并不是隨意的幌蚊,我們需要保證在當前子類實例的成員初始化完成后才能調(diào)用父類的初始化方法:

 class Cat {
     var name: String
     init() {
         name = "cat"
     }
 }

 class Tiger: Cat {
     let power: Int
     override init() {
         power = 10
         super.init()
         name = "tiger"
     }
 }

一般來說,子類的初始化順序是:

  • 1.設置子類自己需要初始化的參數(shù)溃卡,power = 10
  • 2.調(diào)用父類的相應的初始化方法溢豆,super.init()
  • 3.對父類中的需要改變的成員進行設定,name = "tiger"

其中第三步是根據(jù)具體情況決定的瘸羡,如果我們在子類中不需要對父類的成員做出改變的話漩仙,就不存在第 3 步。而在這種情況下犹赖,Swift 會自動地對父類的對應 init 方法進行調(diào)用队他,也就是說,第 2 步的 super.init() 也是可以不用寫的 (但是實際上還是調(diào)用的峻村,只不過是為了簡便 Swift 幫我們完成了)漱挎。這種情況下的初始化方法看起來就很簡單:

 class Cat {
     var name: String
     init() {
         name = "cat"
     }
 }
 
 class Tiger: Cat {
     let power: Int
     override init() {
         power = 10
         // 如果我們不需要改變 name 的話,
         // 雖然我們沒有顯式地對 super.init() 進行調(diào)用
         // 不過由于這是初始化的最后了雀哨,Swift 替我們自動完成了
     }
 }

了解一下指定磕谅、便利、必要初始化

我們在深入初始化方法之前雾棺,不妨先再想想 Swift 中的初始化想要達到一種怎樣的目的膊夹。

其實就是安全。在 Objective-C 中捌浩,init 方法是非常不安全的:沒有人能保證 init 只被調(diào)用一次放刨,也沒有人保證在初始化方法調(diào)用以后實例的各個變量都完成初始化,甚至如果在初始化里使用屬性進行設置的話尸饺,還可能會造成各種問題进统,雖然 Apple 也明確說明了不應該在 init 中使用屬性來訪問助币,但是這并不是編譯器強制的,因此還是會有很多開發(fā)者犯這樣的錯誤螟碎。

所以 Swift 有了超級嚴格的初始化方法眉菱。一方面,Swift 強化了 designated 初始化方法的地位掉分。Swift 中不加修飾的 init 方法都需要在方法中保證所有非 Optional 的實例變量被賦值初始化俭缓,而在子類中也強制 (顯式或者隱式地) 調(diào)用 super 版本的 designated 初始化,所以無論如何走何種路徑酥郭,被初始化的對象總是可以完成完整的初始化的华坦。

 class ClassA {
     let numA: Int
     init(num: Int) {
         numA = num
     }
 }
 
 class ClassB: ClassA {
     let numB: Int
 
     override init(num: Int) {
         numB = num + 1
         super.init(num: num)
     }
 }
在上面的示例代碼中,注意在 init 里我們可以對 let 的實例常量進行賦值不从,這是初始化方法的重要特點惜姐。在 Swift 中 let 聲明的值是常量,無法被寫入賦值椿息,這對于構建線程安全的 API 十分有用载弄。而因為 Swift 的 init 只可能被調(diào)用一次,因此在 init 中我們可以為常量進行賦值撵颊,而不會引起任何線程安全的問題。


 class ClassA {
     let numA: Int
     init(num: Int) {
         numA = num
     }
 
     convenience init(bigNum: Bool) {
         self.init(num: bigNum ? 10000 : 1)
     }
 }
 
 class ClassB: ClassA {
     let numB: Int
 
     override init(num: Int) {
         numB = num + 1
         super.init(num: num)
     }
 }

只要在子類中實現(xiàn)重寫了父類 convenience 方法所需要的 init 方法的話惫叛,我們在子類中就也可以使用父類的 convenience 初始化方法了倡勇。比如在上面的代碼中,我們在 ClassB 里實現(xiàn)了 init(num: Int) 的重寫嘉涌。這樣妻熊,即使在 ClassB 中沒有 bigNum 版本的 convenience init(bigNum: Bool),我們?nèi)匀贿€是可以用這個方法來完成子類初始化:

  let anObj = ClassB(bigNum: true)
  // anObj.numA = 10000, anObj.numB = 10001

因此進行一下總結(jié)仑最,可以看到初始化方法永遠遵循以下兩個原則:

    1. 初始化路徑必須保證對象完全初始化扔役,這可以通過調(diào)用本類型的 designated 初始化方法來得到保證。
    1. 子類的 designated 初始化方法必須調(diào)用父類的 designated 方法警医,以保證父類也完成初始化亿胸。

對于某些我們希望子類中一定實現(xiàn)的 designated 初始化方法,我們可以通過添加 required 關鍵字進行限制预皇,強制子類對這個方法重寫實現(xiàn)侈玄。這樣做的最大的好處是可以保證依賴于某個 designated 初始化方法的 convenience 一直可以被使用。一個現(xiàn)成的例子就是上面的 init(bigNum: Bool):如果我們希望這個初始化方法對于子類一定可用吟温,那么應當將 init(num: Int) 聲明為必須序仙,這樣我們在子類中調(diào)用 init(bigNum: Bool) 時就始終能夠找到一條完全初始化的路徑了:

  class ClassA {
      let numA: Int
      required init(num: Int) {
          numA = num
      }
  
      convenience init(bigNum: Bool) {
          self.init(num: bigNum ? 10000 : 1)
      }
  }
  
  class ClassB: ClassA {
      let numB: Int
  
      required init(num: Int) {
          numB = num + 1
          super.init(num: num)
      }
  }

另外需要說明的是,其實不僅僅是對 designated 初始化方法鲁豪,對于 convenience 的初始化方法潘悼,我們也可以加上 required 以確保子類對其進行實現(xiàn)律秃。這在要求子類不直接使用父類中的 convenience 初始化方法時會非常有幫助。

自定義控件時治唤,重寫父類的初始化棒动,必須提供必要初始化方法,否則會報錯

當我們在自定義控件時肝劲,我首先想到的是迁客,重寫父類的初始化。如下面的代碼:

 import UIKit
 
 class ZBTabBar: UITabBar {
   
     // 視圖初始化
     override init(frame: CGRect) {
         
         super.init(frame: frame)

     }


 }

但是會報一個錯誤如下:


錯誤的意思是:必須提供父類必要初始化的方法

 required init?(coder aDecoder: NSCoder) {
         fatalError("init(coder:) has not been implemented")
     }

由此辞槐,我們知道掷漱,自定義控件時,重寫父類的初始化榄檬,必須提供必要初始化方法卜范。

Swift:為什么必須添加init?(coder decoder: NSCoder)的原因

當我們重寫類的時候經(jīng)常提示要添加代碼:

 required init?(coder aDecoder: NSCoder) {
         fatalError("init(coder:) has not been implemented")
     }

這個叫必要初始化器,這種情況一般會出現(xiàn)在繼承了遵守NSCoding protocol的類鹿榜,比如UIView系列的類海雪、UIViewController系列的類。


為什么一定要添加:

這是NSCoding protocol定義的舱殿,遵守了NSCoding protoaol的所有類必須繼承奥裸。只是有的情況會隱式繼承,而有的情況下需要顯示實現(xiàn)沪袭。


什么情況下要顯示添加:

當我們使用storyboard實現(xiàn)界面的時候湾宙,程序會調(diào)用這個初始化器。注意要去掉fatalError冈绊,fatalError的意思是無條件停止執(zhí)行并打印侠鳄。在obj-c中可以通過下面代碼實現(xiàn)

 NSException *exception = [NSException exceptionWithName:@"HotTeaException" reason:@"The tea is too hot" userInfo:nil];
 @throw exception;

總結(jié):

如果代碼實現(xiàn)界面,那么我們只要根據(jù)編譯器提示添加必要初始化器后死宣,就不用理會伟恶,我們創(chuàng)建界面的工作可以在自定義的初始化器里實現(xiàn)。

補充:

let vc = UIViewController()方式初始化類

UIViewController類視乎只有兩個初始化器:

  • 一個是必要初始化器init?(coder aDecoder: NSCoder)
  • 一個是指定初始化器init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?)

疑問:
那么為什么我們可以用let vc = UIViewController()這種方式初始化類呢毅该?

**答: **原因可能是這個初始化方式是來自uikit,也就是調(diào)用了Object-c下的UIViewController初始化方法博秫,是object-c bridge過來的。

學習地址:

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末台盯,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子畏线,更是在濱河造成了極大的恐慌静盅,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蒿叠,居然都是意外死亡明垢,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進店門市咽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來痊银,“玉大人,你說我怎么就攤上這事施绎∷莞铮” “怎么了?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵谷醉,是天一觀的道長致稀。 經(jīng)常有香客問我,道長俱尼,這世上最難降的妖魔是什么抖单? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮遇八,結(jié)果婚禮上矛绘,老公的妹妹穿的比我還像新娘。我一直安慰自己刃永,他們只是感情好货矮,可當我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著斯够,像睡著了一般囚玫。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上雳刺,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天,我揣著相機與錄音裸违,去河邊找鬼掖桦。 笑死,一個胖子當著我的面吹牛供汛,可吹牛的內(nèi)容都是我干的枪汪。 我是一名探鬼主播,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼怔昨,長吁一口氣:“原來是場噩夢啊……” “哼雀久!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起趁舀,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤赖捌,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后矮烹,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體越庇,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡罩锐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了卤唉。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片涩惑。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖桑驱,靈堂內(nèi)的尸體忽然破棺而出竭恬,到底是詐尸還是另有隱情,我是刑警寧澤熬的,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布痊硕,位于F島的核電站,受9級特大地震影響悦析,放射性物質(zhì)發(fā)生泄漏寿桨。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一强戴、第九天 我趴在偏房一處隱蔽的房頂上張望亭螟。 院中可真熱鬧,春花似錦骑歹、人聲如沸预烙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽扁掸。三九已至,卻和暖如春最域,著一層夾襖步出監(jiān)牢的瞬間谴分,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工镀脂, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留牺蹄,地道東北人。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓薄翅,卻偏偏與公主長得像沙兰,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子翘魄,可洞房花燭夜當晚...
    茶點故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內(nèi)容