Swift 5.x 類的繼承和初始化

  • 所有類的存儲屬性(包括從它的父類繼承的所有屬性)都必須在初始化期間分配初始值
  • Swift為類類型定義了兩種初始化器以確保所有的存儲屬性接收一個初始值. 這些就是所謂的指定初始化器和便捷初始化器
  • 指定初始化器是類的主要初始化器. 指定的初始化器可以初始化所有那個類引用的屬性并且條用合適的父類初始化器來繼續(xù)這個初始化過程給父類鏈
  • 類偏向于少量初始化器, 并且一個類通常只有一個指定初始化器. 指定初始化器是初始化開始并持續(xù)初始化過程到父類鏈的"傳送"點(diǎn)
  • 每個類至少得有一個指定初始化器. 如同在初始化器的自動繼承里描述的那樣, 在某些情況下, 這些需求通過從父類繼承一個或多個指定初始化器來滿足.
  • 便捷初始化器(convenience)是次要的. 你可以在相同的類里定義一個便捷初始化器來調(diào)用一個指定的初始化器作為便捷初始化器來給指定初始化器設(shè)置默認(rèn)形式參數(shù). 你也可以為具體的使用情況或輸入的值類型定義一個便捷初始化器從而創(chuàng)建這個類的實(shí)例.
  • 如果你的類不需要便捷初始化器你可以不提供它. 在為通用的初始化模式創(chuàng)建快捷方式以節(jié)省時間或者類的初始化更加清晰明了的時候使用便捷初始化器

注: 最開始接觸Swift 2.x的時候, 那會我對便捷初始化的稱呼是: 便利構(gòu)造函數(shù), 其實(shí)一個意思

指定初始化器和便捷初始化器
  • 用于值類型的簡單初始化器相同的方式來寫類的指定初始化器
  • convenience修飾符放在init關(guān)鍵字前定義便捷初始化器

指定初始化器語法

init(parameters) {
    statements
}

便捷初始化器語法

convenience init(parameters) {
    statements
}
類的初始化委托
  • 指定初始化器必須從它的直系父類調(diào)用指定初始化器
  • 便捷初始化器必須從相同的類里調(diào)用另一個初始化器
  • 便捷初始化器最終必須調(diào)用一個指定初始化器

類的初始化委托.png


兩段式初始化
  • Swift的類初始化是一個兩段式過程. 在第一個階段, 每一個存儲屬性被引入類分配了一個初始值. 一旦每個存儲屬性的初始狀態(tài)被確定, 第二個階段就開始了, 每個類都有機(jī)會在新的實(shí)例準(zhǔn)備使用之前來定制它的存儲屬性
  • 指定或便捷初始化器在類中被調(diào)用
  • 為這個類的新實(shí)例分配內(nèi)存. 內(nèi)存還沒有被初始化
  • 這個類的指定初始化器確保所有由此類引入的存儲屬性都有一個值. 現(xiàn)在這些存儲屬性的內(nèi)存被初始化
  • 指定初始化器上交父類的初始化器為其存儲屬性執(zhí)行相同的任務(wù)
  • 這個調(diào)用父類初始化器的過程將沿著初始化器鏈一直向上進(jìn)行, 知道到達(dá)初始化器鏈的最頂部
  • 一旦到達(dá)了初始化器鏈的最頂部, 在鏈頂部的類確保所有的存儲屬性都有一個值, 此實(shí)例的內(nèi)存被認(rèn)為完全初始化了, 此時第一階段完成
  • 從頂部初始化器往下, 鏈中的每一個指定初始化器都有機(jī)會進(jìn)一步定制示例. 初始化器現(xiàn)在能夠訪問self并且可以修改它的屬性, 調(diào)用它的實(shí)例方法等等
  • 最終, 鏈中任何便捷初始化器都有機(jī)會定制實(shí)例以及使用self, 此時第二階段完成
  • 兩段式初始化過程的使用讓初始化更加安全, 同時在每個類的層級結(jié)構(gòu)給與了完備的靈活性. 兩段式初始化過程可以防止屬性值在初始化期間被訪問, 還可以防止屬性值被另一個初始化器意外的賦予不同的值.

安全檢查
  1. 指定初始化器必須保證在向上委托給父類初始化器之前, 其所在類引入的所有屬性都要初始化完成.
  2. 指定初始化器必須向上委托父類初始化器, 然后才能為繼承的屬性設(shè)置新值. 如果不這樣做, 指定初始化器富裕的新值將被父類中的初始化器鎖覆蓋.
  3. 便捷初始化器必須先委托同類中的其它初始化器, 然后再為任意屬性賦新值(包括同類里定義的屬性). 如果沒這么做, 便捷初始化器賦予的新值將被自己類中其它指定初始化器所覆蓋.
  4. 初始化器在第一階段初始化完成之前, 不能調(diào)用任何實(shí)例方法、不能讀取任何實(shí)例屬性的值, 也不能引用self作為值.

e.g. 安全檢查
  • 錯誤1

class Person {
    var name: String
    var age: Int
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
    
    convenience init() {
        self.init(name: "unKonw", age: 0)
    }
}

class Teacher: Person {
    var salary: Int
    init(name: String, age: Int, salary: Int) {
        super.init(name: name, age: age)
        self.salary = salary
        self.name = name + "老師"
    }
    
    convenience init(name: String) {
        self.init(name: name, age: 30, salary: 5000)
    }
    
    func showInfo() -> Void {
        print("name: \(name) age: \(age) salary: \(salary)")
    }
}

這個示例違背了第1條安全檢查, 錯誤代碼:

error1.png

Teacher類中, salary是該類下新增屬性, 在調(diào)用super初始化器前, 并未完成salary屬性的初始化
所以需要將salary屬性初始化放在調(diào)用super之前

正確的代碼:

  init(name: String, age: Int, salary: Int) {
        self.salary = salary
        super.init(name: name, age: age)
  }
  • 錯誤2:

error2.png

其余代碼部分一致, 只是將name屬性初始化的位置進(jìn)行了調(diào)整, nameTeacher父類的屬性, 這種寫法違背了安全檢查第2條, 父類的屬性需要在調(diào)用父類的初始化器后才能賦予新值, 所以正確寫法是:

  init(name: String, age: Int, salary: Int) {
        self.salary = salary
        super.init(name: name, age: age)
        self.name = name + "老師"
    }
  • 錯誤3:

error3.png

Teacher的便捷初始化器下賦予name屬性新值, 因?yàn)?code>name是父類屬性, 同樣需要在調(diào)用父類的初始化器后才能賦予新值, 而又因?yàn)槭窃诋?dāng)前類的便利初始化器中, 需要通過調(diào)用自身的指定初始化器完成初始化
所以正確的寫法是:

  convenience init(name: String) {
        self.init(name: name, age: 30, salary: 5000)
        self.name = name + "老師"
    }
  • 錯誤4:

error4.png

第4條, 在初始化完成前調(diào)用了實(shí)例方法
正確方式:

  init(name: String, age: Int, salary: Int) {
        self.salary = salary
        super.init(name: name, age: age)
        self.name = name + "老師"
        self.showInfo()
    }


初始化器的繼承和重寫
  • 不像在Object-C中的子類, Swift的子類不會默認(rèn)繼承父類的初始化器. Swift的這種機(jī)制防止父類的簡單初始化器被一個更專用的子類繼承并被用來創(chuàng)建一個沒有完全或錯誤初始化的新實(shí)例的情況發(fā)生. 只有在特定情況下才會繼承父類的初始化器
  • 如果你想自定義的子類來實(shí)現(xiàn)一個或多個和父類相同的初始化器, 你可以在子類中為那些初始化器提供定制的實(shí)現(xiàn).
  • 當(dāng)你寫的子類初始化器匹配父類指定初始化器的時候, 你實(shí)際上可以重寫那個初始化器. 因此, 在子類的初始化器定義之前你必須寫override修飾符. 如同默認(rèn)初始化器所描述的那樣, 即使是自動提供的默認(rèn)初始化器你也可以重寫.

初始化器的自動繼承
  • 如果你的子類沒有定義任何指定初始化器, 他會自動繼承父類所有的指定初始化器.
  • 如果你的子類提供了所有父類指定初始化器的實(shí)現(xiàn)--要么是通過規(guī)則1繼承來的, 要么通過在定義中提供自定義實(shí)現(xiàn)的--那么它自動繼承所有的父類便捷初始化器.

可失敗初始化器
  • 定義類艾君、結(jié)構(gòu)體或枚舉初始化時可以失敗, 在某些情況下會管大用.這個失敗可能由以下幾種方式觸發(fā), 包括給初始化傳入無效的形式參數(shù)值, 或缺少某種外部所需的資源, 又或是其他阻止初始化的情況.
  • 為了妥善處理這種可能失敗的情況, 在類梳毙、結(jié)構(gòu)體或枚舉中定義一個或多個可失敗的初始化器. 通過在init關(guān)鍵字后面添加問號init?來實(shí)現(xiàn)

必要初始化器
  • 在類的初始化器前添加required修飾符來表明所有該類的子類都必須實(shí)現(xiàn)該初始化器.

反初始化
  • 在類實(shí)例被釋放的時候, 反初始化器就會立即被調(diào)用. 你可以使用deinit關(guān)鍵字來寫反初始化器, 就如同寫初始化器用init關(guān)鍵字一樣. 反初始化器只在類類型中有效
  • 反初始化器會在實(shí)例被釋放之前自動被調(diào)用. 你不能自定調(diào)用反初始化器. 父類的反初始化器可以被子類繼承, 并且子類的反初始化器實(shí)現(xiàn)結(jié)束之后父類的反初始化器會被調(diào)用. 父類的反初始化器總會被調(diào)用, 就算子類沒有反初始化器.
  • 每個類當(dāng)中只能有一個反初始化器. 反初始化器不接收任何形式參數(shù), 并且不需要寫圓括號.

    deinit {
        
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末屑咳,一起剝皮案震驚了整個濱河市苏揣,隨后出現(xiàn)的幾起案子氯夷,更是在濱河造成了極大的恐慌边器,老刑警劉巖启妹,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件筛严,死亡現(xiàn)場離奇詭異,居然都是意外死亡饶米,警方通過查閱死者的電腦和手機(jī)桨啃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來檬输,“玉大人照瘾,你說我怎么就攤上這事∩ゴ龋” “怎么了析命?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我鹃愤,道長簇搅,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任昼浦,我火速辦了婚禮馍资,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘关噪。我一直安慰自己,他們只是感情好乌妙,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布使兔。 她就那樣靜靜地躺著,像睡著了一般藤韵。 火紅的嫁衣襯著肌膚如雪虐沥。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天泽艘,我揣著相機(jī)與錄音欲险,去河邊找鬼。 笑死匹涮,一個胖子當(dāng)著我的面吹牛天试,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播然低,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼喜每,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了雳攘?” 一聲冷哼從身側(cè)響起带兜,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎吨灭,沒想到半個月后刚照,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡喧兄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年无畔,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片繁莹。...
    茶點(diǎn)故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡檩互,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出咨演,到底是詐尸還是另有隱情闸昨,我是刑警寧澤,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站饵较,受9級特大地震影響拍嵌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜循诉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一横辆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧茄猫,春花似錦狈蚤、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽浮创。三九已至蜂嗽,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留幻捏,地道東北人。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓命咐,卻偏偏與公主長得像篡九,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子侈百,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評論 2 355