Swift 構造與析構
前言
- 與 OC 一樣,Swift 中也存在構造和析構過程。不同的是,OC 中的構造方法和析構方法只是普通的方法,而 Swift 中構造器和析構器是一種特殊的結構。
1、構造器
在 Swift 中,類或者結構體在初始化時必須保證它們中包含的所有屬性都被初始化,構造器用來初始化類或者結構體中的屬性,在形式上構造器和方法很像,但是它並不是一個方法。
- 聲明構造器不需要
func
關鍵字。 - 和 OC 類似,在 Swift 中使用
init
表義構在器。 - 在一個類或者結構體中可以有多個構造器,所有的構造器都叫
init
,系統通過重載的特性來判斷使用那個構造器。 - 在參數命名上可以使用和屬性相同的名稱做對應,如果構造器中參數的名稱和屬性的名稱相同,則使用
self
在類中 Swift 提供三種構造器來做初始化:指定構造器、便利構造器和可失敗構造器。
// 指定構造器 init(參數名1: 參數類型, 參數名2: 參數類型, ...) { statements }
// 便利構造器 convenience init(參數名1: 參數類型, 參數名2: 參數類型, ...) { statements }
// 可失敗構造器 init?(參數名1: 參數類型, 參數名2: 參數類型, ...) { statements }
- 聲明構造器不需要
在某些情況下系統會自動生成一個構造器。
- 如果是在某個類中,當類中的所有屬性都有初始值,並且類中沒有定義構造器時,就會得到一個沒有參數的構造器
init()
- 如果是在結構體中,當類中的所有屬性都有初始值,並且結構體中沒有定義構造器時,就會得到一個默認將所有屬性作為參數的構造器。
- 如果是在某個類中,當類中的所有屬性都有初始值,並且類中沒有定義構造器時,就會得到一個沒有參數的構造器
使用構造器時註意事項。
- 在任何構造器完成時,必須保證所有的屬性都被初始化了,即便可選型屬性的值是
nil
,也算它有值。 - 調用類中的方法和屬性必須在初始化完成之後才能進行。
- 在任何構造器完成時,必須保證所有的屬性都被初始化了,即便可選型屬性的值是
在 Swift 中采用繼承式初始化方式。
- 父類和子類的構造器進行交互時,是通過各自的指定構造器進行交互的。
- 便捷構造器只關心本類中的其它構造器,而不關心父類或者子類的構造器。
- 如果在構造器前加上
required
關鍵字,那麽這個類的子類就必須實現它的這個構造器。
1.1 指定構造器
指定構造器包含系統自動生成的構造器和除便利構造器及可失敗構造器之外的所有構造器,是默認的初始化方法。
指定構造器必須在
init
中調用父類的指定構造器,而不能調用其自身的其它構造器。init(參數名1: 參數類型, 參數名2: 參數類型, ...) { statements }
如果沒有在類中實現任何指定構造器,那麽將繼承父類中的所有指定構造器。
// 父類 class Transport { var scope = "" init() {} // 無參數指定構造器 init(str: String) { // 有一個參數的指定構造器 self.scope = str } }
// 子類 class Car: Transport { // 沒有在類中實現任何指定構造器 }
// 使用 let myCar = Car() // 使用了父類中的無參數構造器 let myNewCar = Car(str: "ludi") // 使用父類中有一個參數的構造器
一旦在子類中創建了自己的指定構造器,將不能再使用父類中的構造器,並且子類中的指定構造器聲明中需要調用父類中的某個指定構造器。
// 父類 class Transport { var scope = "" init() {} // 無參數指定構造器 init(str: String) { // 有一個參數的指定構造器 self.scope = str } }
// 子類 class Car: Transport { var wheel = "pulisitong" init(scope: String, wheel: String) { // 創建自己的指定構造器 super.init() // 指定構造器必須調用父類的指定構造器 self.scope = scope self.wheel = wheel } }
// 使用 let myCar = Car(scope: "ludi", wheel: "miqilin") // 此時不能使用父類中無參或者有一個參數的構造器
1.2 便利構造器
調用其它構造器的構造器就叫便利構造器。
- 便利構造器必須調用一個其它的構造器。
- 便利構造器必須直接或者間接調用指定構造器之後才能訪問其它值,它還可以通過調用其它便利構造器來間接調用指定構造器。
如果一個便利構造器想要調用指定構造器,則它必須並且只能調用本類中的指定構造器,而不能調任何父類的構造器。
convenience init(參數名1: 參數類型, 參數名2: 參數類型, ...) { statements }
如果子類中重寫了父類中所有的指定構造器,那麽將繼承父類中所有的便利構造器。
// 父類 class Transport { var scope = "" init() {} // 無參數指定構造器 init(str: String) { // 有一個參數的指定構造器 self.scope = str } convenience init(scope: String) { // 便利構造器 self.init(str: scope) // 調用自身類的指定構造器 } }
// 子類 class Car: Transport { override init() { // 重寫父類指定構造器 super.init() // 指定構造器必須調用父類的指定構造器 } override init(str: String) { // 重寫父類指定構造器 super.init() // 指定構造器必須調用父類的指定構造器 self.scope = "Car" + str } }
// 使用 let myCar = Car(scope: "ludi") // 調用父類的便利構在器
1.3 可失敗構造器
有一些初始化方法允許失敗,並且返回
nil
。可失敗構造器的定義中
init
後面跟著一個?
。init?(參數名1: 參數類型, 參數名2: 參數類型, ...) { statements }
通常面對這種可失敗構造器建議使用
if-let
或guard-let-else
結構,如果初始化成功就執行操作,否則就做其它操作。if-let
結構if let image = UIImage(named: "test") { // 執行與 image 有關的代碼 } else { // 執行與 image 無關的代碼 }
guard-let-else
結構guard let image = UIImage(named: "test") else { // 執行與 image 無關的代碼 } // 執行與 image 有關的代碼
1.4 匿名構造器
有些時候某種對象的子類我們只創建一次,沒有必要專門寫一個構造器。
使用閉包的形式創建一個匿名的構造器,閉包後需要加 (),用來告訴 Swift 需要立刻執行此閉包,否則會把閉包本身作為值賦給了屬性。
let button: UIButton = { let button = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) button.backgroundColor = UIColor.white return button }()
使用其它對象方法創建一個匿名的構造器。
let someArray = ["1", "2", "3"] let someString = someArray.joined(separator: ", ") print(someString) // 1, 2, 3
2、析構器
和構造器對應,Swift 還提供了析構器,用於釋放對象。
- 析構器只適用於類。
- 一個類中只能定義一個析構器。
析構器的定義和構造器很像,使用關鍵字
deinit
,並且析構器沒有參數。deinit() { // 執行析構過程 }
在把對象設置為
nil
時,系統會自動調用析構器。deinit { self.conn.close() self.conn = nil }
由於 Swift 中引入了自動引用計數(ARC),因此不需要讀者手動管理內存,但在使用一些自己的資源的時候,需要使用析構器。
- 比如我要建立一個數據庫訪問類,在初始化時打開鏈接,如果程序退出,連接不釋放,資源就會浪費。
值類型沒有析構是因為值類型不會出現 “共享”,每個值類型只有一個擁有者。
- 比如某個方法,值類型會在其擁有者被系統回收時一同回收,我們不需要在意值類型的生命周期。
Swift 構造與析構