Swift 的構造器(一)

前言

? ? ? 本文主要是Swift的構造器相關知識,另加少部分的OC中的init方法(還有少許Java相關的構造方法),通過兩者的對比來加深對Swift構造器的理解,畢竟Swift是要淘汰的OC的,所以主要研究Swift也是必然。

指定構造器和便利構造器

? ? ? ? Swift構造器分為兩類,一類是指定構造器(Designated Initializer),另一類是便利構造器(Convenience Initializer)。

? ? ? 指定構造器

? ? ? ? 指定構造器在一個類中必須至少有一個, 因為它是類的最主要構造器,沒有之一,所有類的實例的初始化必然會調用指定構造器。以init開頭,參數名、參數列表可以依照實際情況編寫。當沒有自定義指定構造器時,系統會自動生成一個不帶參數的默認構造器,不帶參數的默認構造器,意味著類的定義中,成員變量一定要賦上默認值。當有自定義指定構造器時,類的定義中,成員變量可以沒有默認值,但是會在調用指定構造器時給其賦值,并且賦值時機要在調用父類的指定構造器之前(如果這個類有父類時)。舉個例子,先定義一個基類。

```

class Person {

? ? ? // 成員變量 name 、 sex都是沒有賦值

? ? ? ?var name:String

? ? ? ?var sex:String

? ? ? ?init(name:String, sex:String) {

? ? ? ? ? ? // 在指定構造器中給成員變量賦值

? ? ? ? ? ? self.name = name

? ? ? ? ? ? self.sex = sex

? ? ? ?}

}

```

? 在這個基類Person中,成員變量 name和sex在定義時都沒有賦值,只是指定數據類型,相關的賦值是在指定構造器init(name:String, sex:String)中完成的。如果把Person類改成下面這樣

```

? ?// 這是個錯誤的定義

? ? class Person {

? ? ? ? ? var name:String

? ? ? ? ? var sex:String

? ? }

```

這個時候Xcode直接報錯:

這里報錯第一行顯示是Person沒有構造器,其他行顯示name和sex沒有初始值,其實本質來說,是因為成員變量在Swift中已經不是自動生成默認值,需要程序員自己指定。這里就需要提到一個不管是在Swift或者OC,還是Java或是其他面向對象的語言都有的概念:某個類的實例被創建,其成員變量一定要有值。在上面的錯誤定義中,因為Swift不再為成員變量自動生成默認值,如果程序員再不指定,這樣在創建實例時成員變量會沒有值,結果當然是直接就報錯了。如果我們像下面這樣稍微改動一下:

```

class Person {

? ? ? var name:String="李磊"

? ? ? var sex:String="男"

}

// 調用了Person自動生成的默認指定構造器(無參數)

var per = Person()

```

結果成功運行。在生成了per實例時,調用了自動生成的默認指定構造器,雖說是無參數,但是Person類在定義時,直接就給name和sex賦值了,所以這個per實例是成功被創建了。這里有一個不得不強調的一點,Swift的指定構造器本質是,確保本類的成員變量一定要被賦值,不是說一定要通過指定構造器來賦值。這個從上面改動的例子中可以看出(默認構造器并沒有給成員變量賦值)。

? ? ? ?當某個類有父類時,在其指定構造器中必須調用父類的指定構造器,且在調用父類的指定構造器前,必須得確保這個類的成員變量必須得有值。為證明這些,再定義一個Man類繼承自Person類

```

class Man:Person {

? ? ? ?var education:String="本科"

? ? ? ?// age此處并沒有被賦值

? ? ? ?var age:Int

? ? ? ?init(name:String, sex:String, age:Int) {

? ? ? ? ? ? // 在調用父類的指定構造器之前,先給成員變量age賦值

? ? ? ? ? ? self.age=age

? ? ? ? ? ? // 調用父類的指定構造器

? ? ? ? ? ? super.init(nameStr: name, sexStr: sex)

? ? ? ? }

}

```

如果將上面的例子改成,在指定構造器中先調用父類的指定構造器,而后給成員變量age賦值,像下面一樣

```

class Man:Person {

? ? ? ?var education:String="本科"

? ? ? ?// age此處并沒有被賦值

? ? ? ?var age:Int

? ? ? ?init(name:String, sex:String, age:Int) {

? ? ? ? ? ? ? // 先調用父類的指定構造器

? ? ? ? ? ? ?super.init(nameStr: name, sexStr: sex)

? ? ? ? ? ? ? // 再給成員變量age賦值

? ? ? ? ? ? ? self.age=age

? ? ? ? }

}

```

結果Xcode報錯。

因為age在定義的位置并沒有被賦值,所以在調用父類的指定構造器時age無值。對此強調一點:指定構造器在調用父類的構造器前,一定要確保子類引入的成員變量要有值。

? ? ? 便利構造器

? ? ? 由convenience關鍵字修飾,是橫向代理。橫向代理的意思是,convenience構造器中必須調用同一個類中的其他一個構造器,這個構造器是指定構造器或者便利構造器都行,但是,如果是便利構造器的話,convenience構造器通過調用鏈(代理鏈)最終都得調用一個designated構造器。舉個例子,將Person和Man類稍微改一下:

```

? ? ?class Person {

? ? ? ? ? ? var name:String

? ? ? ? ? ? var sex:String

? ? ? ? ? ? init(name:String) {

? ? ? ? ? ? ? ? self.name= name

? ? ? ? ? ? ? ? self.sex="男"

? ? ? ? ? ? }

? ? ? ? ? ? //指定構造器可以有多個,但是至少有一個

? ? ? ? ? ?init(nameStr:String, sexStr:String) {

? ? ? ? ? ? ? ? self.name= nameStr

? ? ? ? ? ? ? ? self.sex= sexStr

? ? ? ? ? ? }

? ? ? ? ? ?//定義便利構造器(使用convenience修飾)

? ? ? ? ? convenience init(nameString:String, sexString:String) {

? ? ? ? ? ? ? ? ? //這里的便利構造器必須調用同類中的指定構造器,因為Person是基類,便利構造器無法沿著構造器的調用鏈調用到父類指定構造器,因為基類是沒有父類的,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) {

? ? ? ? ? ? ?//子類的指定構造器中必須調用父類的指定構造器

? ? ? ? ? ? ?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)

? ? ? ? }

? ? ? ?//定義指定構造器與父類的便利構造器一樣,這里不算重寫

? ? ? convenience init(showStr:String, age:Int) {

? ? ? ? ? ? ?//這里調用的是從父類繼承來的便利構造器

? ? ? ? ? ? ?self.init(speakWord:"hello!")

? ? ? ? ? ? ?self.age= 20

? ? ? ? ? ? ?print("Man---\(showStr)")

? ? ? ?}

}

```

在改過后的Man類中,就不得不提構造器的繼承。之前說過構造器默認是不繼承,但是在有些情況下,會繼承。

1.子類沒有定義任何的指定構造器, 那么就會自動從父類那里繼承所有的指定構造器

2.如果子類中提供了所有父類指定構造器,不管是通過規則1(沒有定義任何指定構造器)繼承來的,還是自定義實現的,它將繼承所有父類的便利構造器

在改后的Man類中,將Person類中的指定構造器都overrider了,滿足第二種情況,所以父類的便利構造器都被繼承了。對于上面的規則1,個人覺得其實還可以改的更通俗點兒,只要是子類沒有定義指定構造器,子類會從父類那里繼承所有的構造器(包括便利構造器),有興趣的可以試下將Man類其他的構造器都刪掉,只保留便利構造器,在編寫便利構造器時,會發現Person類的所有構造方法都已被繼承。還有一點可以看出,在Man類的便利構造器中,調用的是從父類繼承來的便利構造器( 這一句代碼:self.init(speakWord:"hello!") ),在該便利構造器中又調用了“init(nameStr:String, sexStr:String)”這個指定構造器,所以調用便利構造器,最終都會沿著調用鏈調用到一個指定構造器。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 20- 枚舉,枚舉原始值,枚舉相關值,switch提取枚舉關聯值 Swift枚舉: Swift中的枚舉比OC中的枚...
    iOS_恒仔閱讀 2,313評論 1 6
  • 本章將會介紹 存儲屬性的初始賦值自定義構造過程默認構造器值類型的構造器代理類的繼承和構造過程可失敗構造器必要構造器...
    寒橋閱讀 781評論 0 0
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,915評論 18 139
  • 十天前才去看過大舅媽,不曾想昨天她竟然走了。差不多八月二十號,大舅去慶偉哥家幫他看孩子去了,只有舅媽在家。見到我們...
    紫燕風前舞閱讀 705評論 0 0
  • 1.form表單有什么作用?有哪些常用的input 標簽,分別有什么作用? 作用是:收集填寫的信息,提交給網站的后...
    饑人谷_js_chen閱讀 185評論 0 0