在上一節課的末尾,我們最終明確了一個對象的創建過程,那麼,說起來創建一個對象的樣子很像是調用了一個方法,可這個方法到底是什麼呢?
初始化器
沒錯的,當我們初始化了一個類為對象的時候,我們確實調用了一個方法——初始化器。
初始化器其實就是一個特殊規定了的方法,它能夠為類進行初始化。
想像一下,如果沒有初始化器,那麼我們調用的類就一定是千篇一律的,每一次都要先創建對象,再修改它的屬性?
好吧,我承認目前來講我們都是這麼做的。
總之,我們有辦法讓初始化對象的同時就根據不同的屬性來初始化對象,這就依賴了初始化器。
默認的初始化器
我們之前的類都沒有提供初始化器,但因為我們寫的是一個基類,所以編譯器會自動幫我們寫一個,這個初始化器只是按照我們聲明的方式給所有的屬性賦值罷了,所以我們創建對象的時候才會使用空的括號——因為沒有接收參數的初始化器!
下面我們來看看默認初始化器的樣子(實際上沒有樣子,但我們可以寫出一個和默認初始化器完全相同的初始化器來?)
1 2 3 4 5 |
class Weapon { var aaa = 0 init() {} func fire() {} } |
功能
好了,解答了一直以來我們心中的疑惑,那麼這時候再來重新認識一下構造器吧:它能夠讓你在初始化一個對象的同時就傳入一些參數用以幫助你初始化一個對象。
舉個栗子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
class Gun { var ammo:Int var isSafe:Bool init (ammo a:Int,isSafe s:Bool ) { ammo = a isSafe = s } func fire() { print("bang!") } } var gun = Gun(ammo: 53, isSafe: true) |
看,這下我們可以直接聲明不同狀態的對象了!
……等等!這樣雖好,可是如果我不知道彈藥數量呢?不能有個默認值嗎?比如一般手槍彈藥數量都是8發這種?
總要有默認的東西
當然,我們也可以在類裡邊寫上多個初始化器,這樣初始化的方法就有多種了——這是被允許的。
當然,如果你不想這麼做,也可以用便捷初始化器來重複使用同一個初始化器,我們看看它是怎麼運作的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
class Gun { var ammo:Int var isSafe:Bool init (ammo a:Int,isSafe s:Bool ) { ammo = a isSafe = s } convenience init (isSafe s:Bool) { self.init(ammo: 8,isSafe: s) } func fire() { print("bang!") } } var gun = Gun(isSafe: true) |
相對於初始化器,便捷初始化器要加 convenience 前綴,而且最終它要調用初始化器,我就不多說了,看代碼也能明白它是怎麼運作的了。
重載初始化器
說完了便捷初始化器,那麼我們再回過頭看看如果要重載多個初始化器,有沒有什麼要求:
重載初始化器不能是相同的,即標籤、參數、順序必須有一樣是不同的,這樣才能通過編譯。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
class Gun { var ammo:Int var isSafe:Bool init (ammo a:Int,isSafe s:Bool ) { ammo = a isSafe = s } init (ammo1 a:Int,isSafe2 s:Bool) { ammo = a isSafe = s } convenience init (isSafe s:Bool) { self.init(ammo: 8,isSafe: s) } func fire() { print("bang!") } } |
這樣的兩個初始化器雖然相同但是標籤不同,這是可以的。
初始化器與繼承
子類的對像不可能在其父類沒有初始化的情況下給創造出來——這是個悖論。所以說,如果你的類是一個子類,那麼寫初始化器就要先把父類的初始化器調用一遍——要是有參數還得給傳參數以便人家能夠初始化。
如果你的父類也是繼承自其他類,那他的初始化器肯定也是這樣的,所以最終初始化器會調用到一開始的 AnyObject 上,這樣,從繼承樹最頂端開始一級一級往下初始化,最終完成你的這個子類的初始化工作。
這個過程就是大家口中經常提到的“構造函數鏈”——好吧,在 Swift 當中,我們把它叫做“初始化器”,所以這個專業術語就是“初始化鏈”這樣。
調用父類的初始化器
我們使用 super.init() 的方式來調用父類的初始化器,如果初始化器類型相同,你也得把它給重寫掉——使用 override 前綴。
所以,如果你的類是從父類繼承而來,那寫初始化器的時候記得先調用父類的初始化器。
如果父類初始化器有參數呢?
那我們就需要寫一個接收這些參數的初始化器,然後在初始化器裡邊將參數傳給父類的初始化器:
1 2 3 4 5 6 7 |
class SmallGun: Gun { override init (ammo1 ammo:Int,isSafe2 s:Bool) { let a = ammo + 8 super.init(ammo1: a, isSafe2: s) } } |
轉載請保留出處和原文鏈接:https://www.logcg.com/archives/1134.html
註釋