Swift 類構(gòu)造器的使用

這段時(shí)間學(xué)習(xí)Swift,遇到的一大問題 --- 構(gòu)造器init的使用

使用 init 方法的正確姿勢

一、在 Swift 中, 類的初始化有兩種方式, 分別是

 Designated Initializer 譯為指定構(gòu)造器
 Convenience Initializer  譯為便利構(gòu)造器
注意在:指定構(gòu)造器在一個類中必須至少有一個, 而便利構(gòu)造器的數(shù)量沒有限制.
二、指定構(gòu)造器(Designated Initializer)
Designated initializers are the primary initializers for a class. 
A designated initializer fully initializes all properties introduced by that class and calls an appropriate superclass initializer to continue the initialization process up the superclass chain.

指定構(gòu)造器是類的主要構(gòu)造器, 要在指定構(gòu)造器中初始化所有的屬性, 并且要在調(diào)用父類合適的指定構(gòu)造器.

每個類應(yīng)該只有少量的指定構(gòu)造器, 大多數(shù)類只有一個指定構(gòu)造器, 我們使用 Swift 做 iOS 開發(fā)時(shí)就會用到很多 UIKit 框架類的指定構(gòu)造器, 比如說:

init() 
init(frame: CGRect) 
init(style: UITableViewCellStyle, reuseIdentifier: String?) 

當(dāng)定義一個指定構(gòu)造器的時(shí)候, 必須調(diào)用父類的某一個指定構(gòu)造器:

    init(imageName: String, prompt: String = "") {
          super.init(style: .Default, reuseIdentifier: nil)
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
注意在: 在這里我們的指定構(gòu)造器調(diào)用了父類的指定構(gòu)造器 super.init(style: .Default, reuseIdentifier: nil)
三、便利構(gòu)造器(Convenience Initializer)
Convenience initializers are secondary, supporting initializers for a class. 
You can define a convenience initializer to call a designated initializer from the same class as the convenience initializer with some of the designated initializer’s parameters set to default values.
 You can also define a convenience initializer to create an instance of that class for a specific use case or input value type.

便利構(gòu)造器是類的次要構(gòu)造器, 你需要讓便利構(gòu)造器調(diào)用同一個類中的指定構(gòu)造器, 并將這個指定構(gòu)造器中的參數(shù)填上你想要的默認(rèn)參數(shù).
如果你的類不需要便利構(gòu)造器的話, 那么你就不必定義便利構(gòu)造器, 便利構(gòu)造器前面必須加上convenience
關(guān)鍵字.

    convenience init() {
        self.init() // 注意在:這里必須調(diào)用一個指定的構(gòu)造器或者同一個類中定義的其它初始化方法
    }
四、 init規(guī)則

定義 init 方法必須遵循三條規(guī)則

1.指定構(gòu)造器必須調(diào)用它直接父類的指定構(gòu)造器方法.
2.便利構(gòu)造器必須調(diào)用同一個類中定義的其它初始化方法.
3.便利構(gòu)造器在最后必須調(diào)用一個指定構(gòu)造器.
五、init 機(jī)制

在 Swift 中一個實(shí)例的初始化是分為兩個階段的

 第一階段是實(shí)例的所有屬性被初始化.
 第二階段是實(shí)例的所有屬性可以再次的調(diào)整以備之后的使用.

而這與 ObjC 的區(qū)別主要在于第一部分, 因?yàn)樵?ObjC 中所有的屬性如果不賦值都會默認(rèn)被初始化為 nil或者 0,而在 Swift 中可以所有屬性的值由開發(fā)者來指定.

Swift 的編譯器會對初始化的方法進(jìn)行安全地檢查已保證實(shí)例的初始化可以被安全正確的執(zhí)行:

1.指定構(gòu)造器必須要確保所有被類中提到的屬性在代理向上調(diào)用父類的指定構(gòu)造器前被初始化, 之后才能將其它構(gòu)造任務(wù)代理給父類中的構(gòu)造器.
2.指定構(gòu)造器必須先向上代理調(diào)用父類中的構(gòu)造器, 然后才能為任意屬性賦值.
3.指定構(gòu)造器必須先向上代理調(diào)用父類中的構(gòu)造器, 然后才能為任意屬性賦值.
4.便利構(gòu)造器必須先代理調(diào)用同一個類中的其他構(gòu)造器, 然后再為屬性賦值.
5.構(gòu)造器在第一階段構(gòu)造完成之前, 不能調(diào)用任何實(shí)例方法, 不能讀取任何實(shí)例屬性的值,self不能被引用.
六、init 的繼承和重載
Unlike subclasses in Objective-C, Swift subclasses do not inherit their superclass initializers by default. 
Swift’s approach prevents a situation in which a simple initializer from a superclass is inherited by a more specialized subclass and is used to create a new instance of the subclass that is not fully or correctly initialized.

跟 ObjC 不同, Swift 中的子類默認(rèn)不會繼承來自父類的所有構(gòu)造器. 這樣可以防止錯誤的繼承并使用父類的構(gòu)造器生成錯誤的實(shí)例(可能導(dǎo)致子類中的屬性沒有被賦值而正確初始化). 與方法不同的一點(diǎn)是, 在重載構(gòu)造器的時(shí)候, 你不需要添加 override關(guān)鍵字.

雖然子類不會默認(rèn)繼承來自父類的構(gòu)造器, 但是我們也可以通過別的方法來自動繼承來自父類的構(gòu)造器, 構(gòu)造器的繼承就遵循以下的規(guī)則:

如果子類沒有定義任何的指定構(gòu)造器, 那么會默認(rèn)繼承所有來自父類的指定構(gòu)造器.
如果子類提供了所有父類指定構(gòu)造器的實(shí)現(xiàn), 不管是通過規(guī)則 1繼承過來的, 還是通過自定義實(shí)現(xiàn)的, 它將自動繼承所有父類的便利構(gòu)造器.
七、 錯誤分析

錯誤1

Paste_Image.png

如果子類沒有定義任何的指定構(gòu)造器, 那么會默認(rèn)繼承所有來自父類的指定構(gòu)造器.
  • 這個錯誤是因?yàn)槲覀円婚_始雖然沒有為指定構(gòu)造器提供實(shí)現(xiàn), 不過, 因?yàn)橹剌d了指定構(gòu)造器, 所以來自父類的指定構(gòu)造器并不會被繼承.
  • init(coder aDecoder: NSCoder)方法是來自父類的指定構(gòu)造器, 因?yàn)檫@個構(gòu)造器是 required, 必須要實(shí)現(xiàn). 但是因?yàn)槲覀円呀?jīng)重載了 init(), 定義了一個指定構(gòu)造器, 所以這個方法不會被繼承, 要手動覆寫, 這就是第一個錯誤的原因.

**錯誤2:必須調(diào)用一個 UITableViewCell的指定構(gòu)造器. **

Paste_Image.png
指定構(gòu)造器必須調(diào)用它最近父類的指定構(gòu)造器.
  • 所以我們讓這個指定構(gòu)造器調(diào)用super.init(style: UITableViewCellStyle, reuseIdentifier: String?), 解決了這個問題.

錯誤3: convenience 構(gòu)造器不能調(diào)用 super.init

Paste_Image.png
便利構(gòu)造器必須調(diào)用同一個類中定義的其它構(gòu)造器(指定或便利).
  • 我這里將它改為self.init(style: .Default, reuseIdentifier: nil),而這段代碼目前還是有問題的, 而這就是錯誤 4的代碼.

錯誤4

Paste_Image.png
如果子類沒有定義任何的指定構(gòu)造器, 那么會默認(rèn)繼承所有來自父類的指定構(gòu)造器.
  • 錯誤 4的主要原因就是重載了父類的init(coder aDecoder: NSCoder)指定構(gòu)造器, 導(dǎo)致父類的指定構(gòu)造器 init(style: .Default, reuseIdentifier: nil)并沒有被當(dāng)前類TableViewCell繼承(因?yàn)橹剌d了指定構(gòu)造器, 所以來自父類的指定構(gòu)造器并不會被繼承), 所以當(dāng)前類中是沒有 init(style: .Default, reuseIdentifier: nil)指定構(gòu)造器.
  • 只需要刪掉這個 init(coder aDecoder: NSCoder)方法就可以解決這個錯誤了.

錯誤五

Paste_Image.png
指定構(gòu)造器必須要確保所有被類中提到的屬性在代理向上調(diào)用父類的指定構(gòu)造器前被初始化, 之后才能將其它構(gòu)造任務(wù)代理給父類中的構(gòu)造器.
  • 錯誤 5的主要原因是違反了這一條規(guī)則, 它在調(diào)用super.init(style: .Default, reuseIdentifier: nil)之前并沒有初始化自己的所有屬性.
  • let label = UILabel()在屬性定義的時(shí)候就為他說初始化一個值.
八、總結(jié)

Swift 中構(gòu)造器需要遵循的規(guī)則還是很多的, 總結(jié)一下, 有以下規(guī)則:

  • 調(diào)用相關(guān)
1.指定構(gòu)造器必須調(diào)用它直接父類的指定構(gòu)造器方法.
2.便利構(gòu)造器必須調(diào)用同一個類中定義的其它初始化方法.
3.便利構(gòu)造器在最后必須調(diào)用一個指定構(gòu)造器.
  • 屬性相關(guān)
1.指定構(gòu)造器必須要確保所有被類中提到的屬性在代理向上調(diào)用父類的指定構(gòu)造器前被初始化, 之后才能將其它構(gòu)造任務(wù)代理給父類中的構(gòu)造器.
2.指定構(gòu)造器必須先向上代理調(diào)用父類中的構(gòu)造器, 然后才能為任意屬性賦值.
3.便利構(gòu)造器必須先代理調(diào)用同一個類中的其他構(gòu)造器, 然后再為屬性賦值.
4.構(gòu)造器在第一階段構(gòu)造完成之前, 不能調(diào)用任何實(shí)例方法, 不能讀取任何實(shí)例屬性的值,self不能被引用.
  • 繼承相關(guān)
1.如果子類沒有定義任何的指定構(gòu)造器, 那么會默認(rèn)繼承所有來自父類的指定構(gòu)造器.
2.如果子類提供了所有父類指定構(gòu)造器的實(shí)現(xiàn), 不管是通過上一條規(guī)則繼承過來的, 還是通過自定義實(shí)現(xiàn)的, 它將自動繼承所有父類的便利構(gòu)造器.
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

  • 本章將會介紹 存儲屬性的初始賦值自定義構(gòu)造過程默認(rèn)構(gòu)造器值類型的構(gòu)造器代理類的繼承和構(gòu)造過程可失敗構(gòu)造器必要構(gòu)造器...
    寒橋閱讀 778評論 0 0
  • 構(gòu)造過程是使用類、結(jié)構(gòu)體或枚舉類型的實(shí)例之前的準(zhǔn)備過程。在新實(shí)例可用前必須執(zhí)行這個過程,具體操作包括設(shè)置實(shí)例中每個...
    莽原奔馬668閱讀 697評論 0 3
  • 構(gòu)造過程 構(gòu)造過程是使用類、結(jié)構(gòu)體或枚舉類型的實(shí)例之前的準(zhǔn)備過程。在新實(shí)例可用前必須執(zhí)行這個過程,具體操作包括設(shè)置...
    蠱毒_閱讀 738評論 0 2
  • 20- 枚舉,枚舉原始值,枚舉相關(guān)值,switch提取枚舉關(guān)聯(lián)值 Swift枚舉: Swift中的枚舉比OC中的枚...
    iOS_恒仔閱讀 2,313評論 1 6
  • 很多媽媽跟我反應(yīng)自己的孩子非常調(diào)皮難以控制、很抓狂。我不想去揣測孩子到底是有多皮,我想說的是“孩子就是沒有被現(xiàn)代文...
    Tinko王閱讀 171評論 0 0