前言
? ? ? 本文主要是Swift的構(gòu)造器相關(guān)知識,另加少部分的OC中的init方法(還有少許Java相關(guān)的構(gòu)造方法)尤揣,通過兩者的對比來加深對Swift構(gòu)造器的理解疙筹,畢竟Swift是要淘汰的OC的隔盛,所以主要研究Swift也是必然呕臂。
指定構(gòu)造器和便利構(gòu)造器
? ? ? ? Swift構(gòu)造器分為兩類,一類是指定構(gòu)造器(Designated Initializer)查坪,另一類是便利構(gòu)造器(Convenience Initializer)寸宏。
? ? ? 指定構(gòu)造器
? ? ? ? 指定構(gòu)造器在一個類中必須至少有一個, 因?yàn)樗穷惖淖钪饕獦?gòu)造器,沒有之一偿曙,所有類的實(shí)例的初始化必然會調(diào)用指定構(gòu)造器氮凝。以init開頭,參數(shù)名望忆、參數(shù)列表可以依照實(shí)際情況編寫罩阵。當(dāng)沒有自定義指定構(gòu)造器時,系統(tǒng)會自動生成一個不帶參數(shù)的默認(rèn)構(gòu)造器启摄,不帶參數(shù)的默認(rèn)構(gòu)造器稿壁,意味著類的定義中,成員變量一定要賦上默認(rèn)值歉备。當(dāng)有自定義指定構(gòu)造器時傅是,類的定義中,成員變量可以沒有默認(rèn)值蕾羊,但是會在調(diào)用指定構(gòu)造器時給其賦值喧笔,并且賦值時機(jī)要在調(diào)用父類的指定構(gòu)造器之前(如果這個類有父類時)。舉個例子龟再,先定義一個基類书闸。
```
class Person {
? ? ? // 成員變量 name 、 sex都是沒有賦值
? ? ? ?var name:String
? ? ? ?var sex:String
? ? ? ?init(name:String, sex:String) {
? ? ? ? ? ? // 在指定構(gòu)造器中給成員變量賦值
? ? ? ? ? ? self.name = name
? ? ? ? ? ? self.sex = sex
? ? ? ?}
}
```
? 在這個基類Person中利凑,成員變量 name和sex在定義時都沒有賦值浆劲,只是指定數(shù)據(jù)類型嫌术,相關(guān)的賦值是在指定構(gòu)造器init(name:String, sex:String)中完成的。如果把Person類改成下面這樣
```
? ?// 這是個錯誤的定義
? ? class Person {
? ? ? ? ? var name:String
? ? ? ? ? var sex:String
? ? }
```
這個時候Xcode直接報錯:
這里報錯第一行顯示是Person沒有構(gòu)造器梳侨,其他行顯示name和sex沒有初始值蛉威,其實(shí)本質(zhì)來說日丹,是因?yàn)槌蓡T變量在Swift中已經(jīng)不是自動生成默認(rèn)值走哺,需要程序員自己指定。這里就需要提到一個不管是在Swift或者OC哲虾,還是Java或是其他面向?qū)ο蟮恼Z言都有的概念:某個類的實(shí)例被創(chuàng)建丙躏,其成員變量一定要有值。在上面的錯誤定義中束凑,因?yàn)镾wift不再為成員變量自動生成默認(rèn)值晒旅,如果程序員再不指定,這樣在創(chuàng)建實(shí)例時成員變量會沒有值汪诉,結(jié)果當(dāng)然是直接就報錯了废恋。如果我們像下面這樣稍微改動一下:
```
class Person {
? ? ? var name:String="李磊"
? ? ? var sex:String="男"
}
// 調(diào)用了Person自動生成的默認(rèn)指定構(gòu)造器(無參數(shù))
var per = Person()
```
結(jié)果成功運(yùn)行。在生成了per實(shí)例時扒寄,調(diào)用了自動生成的默認(rèn)指定構(gòu)造器鱼鼓,雖說是無參數(shù),但是Person類在定義時该编,直接就給name和sex賦值了迄本,所以這個per實(shí)例是成功被創(chuàng)建了。這里有一個不得不強(qiáng)調(diào)的一點(diǎn)课竣,Swift的指定構(gòu)造器本質(zhì)是嘉赎,確保本類的成員變量一定要被賦值,不是說一定要通過指定構(gòu)造器來賦值于樟。這個從上面改動的例子中可以看出(默認(rèn)構(gòu)造器并沒有給成員變量賦值)公条。
? ? ? ?當(dāng)某個類有父類時,在其指定構(gòu)造器中必須調(diào)用父類的指定構(gòu)造器迂曲,且在調(diào)用父類的指定構(gòu)造器前赃份,必須得確保這個類的成員變量必須得有值。為證明這些奢米,再定義一個Man類繼承自Person類
```
class Man:Person {
? ? ? ?var education:String="本科"
? ? ? ?// age此處并沒有被賦值
? ? ? ?var age:Int
? ? ? ?init(name:String, sex:String, age:Int) {
? ? ? ? ? ? // 在調(diào)用父類的指定構(gòu)造器之前抓韩,先給成員變量age賦值
? ? ? ? ? ? self.age=age
? ? ? ? ? ? // 調(diào)用父類的指定構(gòu)造器
? ? ? ? ? ? super.init(nameStr: name, sexStr: sex)
? ? ? ? }
}
```
如果將上面的例子改成,在指定構(gòu)造器中先調(diào)用父類的指定構(gòu)造器鬓长,而后給成員變量age賦值谒拴,像下面一樣
```
class Man:Person {
? ? ? ?var education:String="本科"
? ? ? ?// age此處并沒有被賦值
? ? ? ?var age:Int
? ? ? ?init(name:String, sex:String, age:Int) {
? ? ? ? ? ? ? // 先調(diào)用父類的指定構(gòu)造器
? ? ? ? ? ? ?super.init(nameStr: name, sexStr: sex)
? ? ? ? ? ? ? // 再給成員變量age賦值
? ? ? ? ? ? ? self.age=age
? ? ? ? }
}
```
結(jié)果Xcode報錯。
因?yàn)閍ge在定義的位置并沒有被賦值涉波,所以在調(diào)用父類的指定構(gòu)造器時age無值英上。對此強(qiáng)調(diào)一點(diǎn):指定構(gòu)造器在調(diào)用父類的構(gòu)造器前炭序,一定要確保子類引入的成員變量要有值。
? ? ? 便利構(gòu)造器
? ? ? 由convenience關(guān)鍵字修飾苍日,是橫向代理惭聂。橫向代理的意思是,convenience構(gòu)造器中必須調(diào)用同一個類中的其他一個構(gòu)造器相恃,這個構(gòu)造器是指定構(gòu)造器或者便利構(gòu)造器都行辜纲,但是,如果是便利構(gòu)造器的話拦耐,convenience構(gòu)造器通過調(diào)用鏈(代理鏈)最終都得調(diào)用一個designated構(gòu)造器耕腾。舉個例子,將Person和Man類稍微改一下:
```
? ? ?class Person {
? ? ? ? ? ? var name:String
? ? ? ? ? ? var sex:String
? ? ? ? ? ? init(name:String) {
? ? ? ? ? ? ? ? self.name= name
? ? ? ? ? ? ? ? self.sex="男"
? ? ? ? ? ? }
? ? ? ? ? ? //指定構(gòu)造器可以有多個杀糯,但是至少有一個
? ? ? ? ? ?init(nameStr:String, sexStr:String) {
? ? ? ? ? ? ? ? self.name= nameStr
? ? ? ? ? ? ? ? self.sex= sexStr
? ? ? ? ? ? }
? ? ? ? ? ?//定義便利構(gòu)造器(使用convenience修飾)
? ? ? ? ? convenience init(nameString:String, sexString:String) {
? ? ? ? ? ? ? ? ? //這里的便利構(gòu)造器必須調(diào)用同類中的指定構(gòu)造器扫俺,因?yàn)镻erson是基類,便利構(gòu)造器無法沿著構(gòu)造器的調(diào)用鏈調(diào)用到父類指定構(gòu)造器固翰,因?yàn)榛愂菦]有父類的,Swift不是OC狼纬,OC中NSObject是所有對象的基類,而Swift是沒有這種“終極”基類的骂际。
? ? ? ? ? ? ? ? ? self.init(nameStr: nameString, sexStr: sexString)
? ? ? ? ? }
? ? ? ? ? convenience init(speakWord:String) {
? ? ? ? ? ? ? ? ? self.init(nameStr:"人類", sexStr:"")
? ? ? ? ? ? ? ? ? print("Person--\(speakWord)")
? ? ? ? ? }
? ? }
class Man:Person{
? ? ? ?var education:String="本科"
? ? ? ?var age:Int= 10
? ? ? ?override init(name:String) {
? ? ? ? ? ? ?//子類的指定構(gòu)造器中必須調(diào)用父類的指定構(gòu)造器
? ? ? ? ? ? ?super.init(name: name)
? ? ? ?}
? ? ? ?override init(nameStr:String, sexStr:String) {
? ? ? ? ? ? ?super.init(nameStr: nameStr, sexStr: sexStr)
? ? ? ?}
? ? ? ?init(name:String, sex:String, age:Int) {
? ? ? ? ? ? self.age= 20
? ? ? ? ? ? super.init(nameStr: name, sexStr: sex)
? ? ? ? }
? ? ? ?//定義指定構(gòu)造器與父類的便利構(gòu)造器一樣,這里不算重寫
? ? ? convenience init(showStr:String, age:Int) {
? ? ? ? ? ? ?//這里調(diào)用的是從父類繼承來的便利構(gòu)造器
? ? ? ? ? ? ?self.init(speakWord:"hello!")
? ? ? ? ? ? ?self.age= 20
? ? ? ? ? ? ?print("Man---\(showStr)")
? ? ? ?}
}
```
在改過后的Man類中疗琉,就不得不提構(gòu)造器的繼承。之前說過構(gòu)造器默認(rèn)是不繼承方援,但是在有些情況下没炒,會繼承。
1.子類沒有定義任何的指定構(gòu)造器, 那么就會自動從父類那里繼承所有的指定構(gòu)造器
2.如果子類中提供了所有父類指定構(gòu)造器犯戏,不管是通過規(guī)則1(沒有定義任何指定構(gòu)造器)繼承來的送火,還是自定義實(shí)現(xiàn)的,它將繼承所有父類的便利構(gòu)造器
在改后的Man類中先匪,將Person類中的指定構(gòu)造器都o(jì)verrider了种吸,滿足第二種情況,所以父類的便利構(gòu)造器都被繼承了呀非。對于上面的規(guī)則1坚俗,個人覺得其實(shí)還可以改的更通俗點(diǎn)兒,只要是子類沒有定義指定構(gòu)造器岸裙,子類會從父類那里繼承所有的構(gòu)造器(包括便利構(gòu)造器)猖败,有興趣的可以試下將Man類其他的構(gòu)造器都刪掉,只保留便利構(gòu)造器降允,在編寫便利構(gòu)造器時恩闻,會發(fā)現(xiàn)Person類的所有構(gòu)造方法都已被繼承。還有一點(diǎn)可以看出剧董,在Man類的便利構(gòu)造器中幢尚,調(diào)用的是從父類繼承來的便利構(gòu)造器( 這一句代碼:self.init(speakWord:"hello!") )破停,在該便利構(gòu)造器中又調(diào)用了“init(nameStr:String, sexStr:String)”這個指定構(gòu)造器,所以調(diào)用便利構(gòu)造器尉剩,最終都會沿著調(diào)用鏈調(diào)用到一個指定構(gòu)造器真慢。