Swift 初始化 init 小結

Swift 的初始化和 Objective-C 有一個很大的不同,Objective-C 默認會給每個屬性賦一個空值,如 nil 或者 0,但 Swift 的初始化更加嚴格,需要開發者自己顯示指定類成員的初始值,否則編譯會報錯。

結構體初始化

如果結構體沒有實現任何初始化函數,Swift 默認給生成一個包含所有成員變量的初始化構造器。

struct RocketConfiguration {
    let name: String = "Athena 9 Heavy"
    let numberOfFirstStageCores: Int
    let numberOfSecondStageCores: Int
    let numberOfStageReuseLandingLegs: Int
}

如果使用如下的代碼初始化這個結構體:

let athena9Heavy = RocketConfiguration()

將產生編譯錯誤,如下圖:

有兩種做法可以消除錯誤,聲明結構體成員時即賦值,如下:

struct RocketConfiguration {
    let name: String = "Athena 9 Heavy"
    let numberOfFirstStageCores: Int = 3
    let numberOfSecondStageCores: Int = 1
    let numberOfStageReuseLandingLegs: Int? = nil
}

let athena9Heavy = RocketConfiguration()

但這種情況下,成員變量無法再修改,除非將let改成var。或者使用默認的初始化構造器,給每個成員賦一個初始化的值:

struct RocketConfiguration {
    let name: String = "Athena 9 Heavy"
    let numberOfFirstStageCores: Int
    let numberOfSecondStageCores: Int
    let numberOfStageReuseLandingLegs: Int
}

let athena9Heavy = RocketConfiguration(numberOfFirstStageCores: 3, numberOfSecondStageCores: 1, nominalBurnTime: nil)

類的初始化

和結構體不同的是,類必須自己聲明初始化構造器:

class LaunchSite {
    let name: String
    let coordinates: (String, String)
    
    init(name: String, coordinates: (String, String)) {
        self.name = name
        self.coordinates = coordinates
    }
}

如果去掉上面的init函數,將產生編譯錯誤,如下圖:

指定構造器(Designated Initializers)

指定構造器對所有沒有默認值的非可選屬性進行初始化。

class RocketComponent {
    let model: String
    let serialNumber: String
    let reusable: Bool
    
    // Init #1a - Designated
    init(model: String, serialNumber: String, reusable: Bool) {
        self.model = model
        self.serialNumber = serialNumber
        self.reusable = reusable
    }
}    

初始化調用:

let payload = RocketComponent(model: "RT-1", serialNumber: "234", reusable: false)

便利構造器(Convenience Initializers)

便利構造器是在init前加一個關鍵子convenience,它為一些屬性提供默認值:

// Init #1b - Convenience
convenience init(model: String, serialNumber: String) {
    self.init(model: model, serialNumber: serialNumber, reusable: false)
}

這樣,在初始化時就無需給所有屬性賦值,如下:

let fairing = RocketComponent(model: "Serpent", serialNumber: "0")

便利構造器通常要調用類自身的便利構造器或者指定構造器,不管是哪種,最終都要調用指定構造器。如下圖:

子類的初始化

class Tank: RocketComponent {
  let encasingMaterial: String
}

這段代碼會產生編譯錯誤:

encasingMaterial 沒有初始化值,Swift 的原則是,所有屬性在初始化階段都要完成賦值,因為 Swift 不會給屬性賦默認值,需要開發者自己完成。簡單做法是給encasingMaterial屬性一個默認值:

class Tank: RocketComponent {
    let encasingMaterial: String = "Aluminum"
}

let fuelTank = Tank(model: "Athena", serialNumber:"003", reusable: true)

或者聲明一個指定構造器:

class Tank: RocketComponent {
    let encasingMaterial: String
    
    init(model: String, serialNumber: String, reusable: Bool, encasingMaterial: String) {
        self.encasingMaterial = encasingMaterial
        super.init(model: model, serialNumber: serialNumber, reusable: reusable)
    }
}

let fuelTank = Tank(model: "Athena", serialNumber:"003", reusable: true, encasingMaterial: "Aluminum")

需要注意的是,子類自身屬性的初始化需要在調用父類的初始化構造器前完成,如下代碼

init(model: String, serialNumber: String, reusable: Bool, encasingMaterial: String) {
        super.init(model: model, serialNumber: serialNumber, reusable: reusable)
        self.encasingMaterial = encasingMaterial
    }

將產生編譯錯誤,如下圖:

父類屬性的初始化,需要在調用父類構造器之后:如下:

init(model: String, serialNumber: String, reusable: Bool, encasingMaterial: String) {
    self.encasingMaterial = encasingMaterial
    super.init(model: model, serialNumber: serialNumber, reusable: reusable)
    self.model = model + "-X"
}

要先調用父類的構造器,以防止先給屬性賦值了然后才調用父類而把以前的賦值覆蓋了。若把self.model放在super.init之前,會產生編譯錯誤:

初始化調用順序

  1. 便利構造器需要調用類自身的便利構造器或指定構造器,最終要調用類自身的指定構造器。
  2. 子類的指定構造器需要調用父類的指定構造器

如下圖:


這篇文章總結自以下兩篇文章
Swift Tutorial: Initialization In Depth, Part 1/2
Swift Tutorial: Initialization In Depth, Part 2/2
想了解更詳細的 Swift 初始化過程可作參考,建議大家創建一個 playground,跑一遍文章中的例子,能加深理解。

其他參考資料:
Swift中文教程(十四) 初始化
Initialization in Swift

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,835評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,676評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,730評論 0 380
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,118評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,873評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,266評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,330評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,482評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,036評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,846評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,025評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,575評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,279評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,684評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,953評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,751評論 3 394
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,016評論 2 375

推薦閱讀更多精彩內容