原文:http://ghui.me/post/2016/05/swift-init/
Swift的構(gòu)造器的編寫規(guī)則要比其它語言麻煩很多, 有很多限制, 有些限制也不是那么容易想明白的, 但我想Swift的設(shè)計(jì)者肯定是有自己的考慮楔脯,不會(huì)平白無故的去設(shè)置這些限制汰规,在這篇文章中我將試著總結(jié)一下Swift語言在構(gòu)造過程中的種種限制以及為什么會(huì)需要這些限制.
1: Swift會(huì)為構(gòu)造器的每個(gè)參數(shù)自動(dòng)生成一個(gè)跟內(nèi)部名字相同的外部名
構(gòu)造器并不像函數(shù)和方法那樣在括號前有一個(gè)可辨別的名字(構(gòu)造器統(tǒng)一叫init), 為了提高構(gòu)造器的可讀性就有了這條限制, 當(dāng)然如果你實(shí)在是不想為構(gòu)造器提供外部名,可以使用下劃線_來顯式描述它的外部名, 這樣就不會(huì)有上述默認(rèn)行為了.
2: 任何一個(gè)構(gòu)造器都必須使當(dāng)前對象內(nèi)的非lazy非Optional類型的存儲(chǔ)屬性獲得確定值
如果你之前做過Java開發(fā), 你可能會(huì)對這一點(diǎn)產(chǎn)生疑惑, 在Java的構(gòu)造器里我們是不需要對所有的字段顯示進(jìn)行賦值的.
其實(shí)這個(gè)和Swift引入的Optional有關(guān),在Swift中非Optional的變量默認(rèn)是不會(huì)有值的, 除非你將它顯示的定義為Optional的, 換句話說Swift中Optional類型的變量才相當(dāng)于Java中的變量(java中的全局變量會(huì)自動(dòng)被賦予默認(rèn)值).而Swift中Optional類型的變量才會(huì)有默認(rèn)值nil, 而且構(gòu)造器的目的就是分配內(nèi)存以及使當(dāng)前對象處于一個(gè)確定的狀態(tài)(屬性都有值包括nil)
3: 如果結(jié)構(gòu)體或類的所有的屬性都有默認(rèn)值, Swift才會(huì)為它們提供默認(rèn)構(gòu)造器
在java中如果你沒有定義構(gòu)造器, Java就會(huì)自動(dòng)為你提供一個(gè)默認(rèn)構(gòu)造器, Swift為什么不?
基實(shí)這個(gè)和限制2的原因是一樣的, 首先構(gòu)造器要保證當(dāng)前對象所有的屬性都有值, 而Swift中的非Optionl類型的變量沒有默認(rèn)值, 所以只有當(dāng)所有屬性都有默認(rèn)值的情況下Swift才能提供默認(rèn)構(gòu)造器.
4: 如果你自定義了構(gòu)造器, 那么默認(rèn)構(gòu)造器會(huì)丟失(如果是結(jié)構(gòu)體苦囱,還將丟失逐一成員構(gòu)造器)
這種限制可以防止你為值類型增加了一個(gè)額外的且十分復(fù)雜的構(gòu)造器之后,仍然有人錯(cuò)誤的使用自動(dòng)生成的構(gòu)造器. 如果你想保留默認(rèn)構(gòu)造器逐一成員構(gòu)造器以及你自己定義的構(gòu)造器,你可以將自定義構(gòu)造器定義到extension中
5. 指定構(gòu)造器與便利構(gòu)造器是針對類而言的, 值類型的構(gòu)造器沒有這一說
6. 指定構(gòu)造器必須總是向上代理, 便利構(gòu)造器必須總是橫向代理
之所以要這樣規(guī)定, 官方的說法是為了簡化指定構(gòu)造器與便利構(gòu)造器之間的調(diào)用關(guān)系:
1: 指定構(gòu)造器必須調(diào)用其直接父類的的指定構(gòu)造器
2: 便利構(gòu)造器必須調(diào)用同一類中定義的其它構(gòu)造器
3: 便利構(gòu)造器必須最終導(dǎo)致一個(gè)指定構(gòu)造器被調(diào)用
7. 兩段式構(gòu)造過程
第一個(gè)階段: 每個(gè)存儲(chǔ)屬性都獲得一個(gè)初始值
第二個(gè)階段: 進(jìn)一步定制存儲(chǔ)屬性的值
兩段式構(gòu)造過程可以防止屬性在初始化之前被訪問, 也可以防止屬性被另外一個(gè)構(gòu)造器意外地賦予不同的值.
這個(gè)怎么理解呢? 若屬性在初始化之前(第一階段結(jié)束之前)被訪問, 顯然這種情況是沒有意義的,因?yàn)檫@個(gè)時(shí)候?qū)傩赃€沒有任何值, 另外一種情況若當(dāng)前被訪問的屬性是從父類繼承過來的, 如果它在初始化之前被訪問,也是沒有意義的, 因?yàn)樗鼤?huì)被后面的super.init方法重新賦予初始值.
我對兩段式構(gòu)造過程的理解: 之所以要分兩段式來構(gòu)造和Swift引入Optional的概念還是分不開的, Swift中的變量默認(rèn)都不是Optional的, 這些變量不像其它語言(如Java)中的那樣會(huì)默認(rèn)有初始值, 所以必須需要第一階段來確定一個(gè)初始值.換個(gè)角度來看, 其它語言的構(gòu)造器相當(dāng)于直接是從第二個(gè)階段開始的, 它們的第一階段相當(dāng)于是在定義屬性時(shí)(這個(gè)時(shí)候系統(tǒng)會(huì)默認(rèn)分配一個(gè)默認(rèn)值, 如果你不顯式的賦予它初始值). 所以其實(shí),如果你在Swift中給你的屬性在定義時(shí)就顯式的賦予了初始值,或者你的屬性是Optional的, 那么你在寫構(gòu)造器時(shí)也就沒有這些限制了, 因?yàn)檫@個(gè)時(shí)候在init方法中你相當(dāng)于是直接從第二個(gè)階段開始的.
8. 4種安全檢查
指定構(gòu)造器必須保證它所在類引入的所有屬性都必須先初始化完成,之后才能將其它構(gòu)造任務(wù)向上代理給父類中的構(gòu)造器.
指定構(gòu)造器必須先向上代理調(diào)用父類構(gòu)造器吃靠,然后再為繼承的屬性設(shè)置新值弓乙。如果沒這么做塘砸,指定構(gòu)造器賦予的新值將被父類中的構(gòu)造器所覆蓋节仿。
便利構(gòu)造器必須先代理調(diào)用同一類中的其它構(gòu)造器,然后再為任意屬性賦新值掉蔬。如果沒這么做廊宪,便利構(gòu)造器賦予的新值將被同一類中其它指定構(gòu)造器所覆蓋。
構(gòu)造器在第一階段構(gòu)造完成之前女轿,不能調(diào)用任何實(shí)例方法箭启,不能讀取任何實(shí)例屬性的值,不能引用self作為一個(gè)值蛉迹。
這4項(xiàng)中只有第一條比較難理解一些傅寡,當(dāng)初困擾了好久,后來在萬能的stackoverflow上找到的答案北救,詳情
9. Swift中的子類默認(rèn)不會(huì)繼承父類的構(gòu)造器
Swift的這種機(jī)制可以防止一個(gè)父類的簡單構(gòu)造器被一個(gè)更精細(xì)的子類繼承荐操,并被錯(cuò)誤地用來創(chuàng)建子類的實(shí)例。父類的構(gòu)造器僅會(huì)在安全和適當(dāng)?shù)那闆r下被繼承珍策。
10. 你只能重寫父類的指定構(gòu)造器, 無法重寫父類的便利構(gòu)造器
為了簡化構(gòu)造器之間的調(diào)用關(guān)系, 便利構(gòu)造器必須總是橫向代理. 也就是說在便利構(gòu)造器中是無法訪問父類的構(gòu)造器的, 也就不能完成從父類中繼承下來的屬性的初始化, 導(dǎo)致無法完成子類的完全初始化, 所以子類是不能重寫父類的便利構(gòu)造器的.
11. 子類不僅可以重寫父類的指定構(gòu)造器, 還可以將其重寫為便利構(gòu)造器
12. 構(gòu)造器的自動(dòng)繼承
若子類中引入的存儲(chǔ)屬性都有缺省值, 且子類中沒有定義任何構(gòu)造方法. 那么子類將會(huì)自動(dòng)繼承父類中所有的構(gòu)造方法(包括便利構(gòu)造方法)
若子類只是重寫了父類中的部分構(gòu)造方法, 則子類不再會(huì)自動(dòng)繼承父類中的其他構(gòu)造方法
若子類重寫了父類中的所有指定構(gòu)造方法, 則子類同時(shí)會(huì)自動(dòng)繼承父類中所有的便利構(gòu)造方法
我的理解: 父類的構(gòu)造器只會(huì)在安全和適當(dāng)?shù)那闆r下被繼承, 滿足上面三種條件中的任何一條, 子類去自動(dòng)繼承父類中的構(gòu)造方法都是適當(dāng)?shù)?不會(huì)引起任何構(gòu)造過程的混亂.
13. 可失敗構(gòu)造器的參數(shù)列表不能與其它非可失敗構(gòu)造器的參數(shù)列表相同
若參數(shù)列表相同, 則可以認(rèn)為兩個(gè)方法是同一個(gè)方法, 而一個(gè)方法不能同時(shí)是非可失敗與可失敗的
14. 可失敗構(gòu)造器,在返回nil前必須要將所有的stroed property賦值
從swift2.2開始已沒有此限制