19.Swift學習之構造函數與析構函數

重要說明

  • 本文中提到的構造函數,在很多書中有其他的說法,如構造器,構造方法,初始化,初始函數等
  • 本文中提到的析構函數,在很多書中有其他的說法,如反構造器,析構方法,反初始化,反初始函數等

構造函數的介紹

  • 構造函數用於初始化一個類的實例(創建對象)
  • 默認情況下載創建一個類時,必然會調用一個構造函數
  • 即便是沒有編寫任何構造函數,編譯器也會提供一個默認的構造函數
  • 如果是繼承自NSObject,可以對父類的構造函數進行重寫

默認構造函數

  • 在創建類和結構體的實例時必須爲所有的存儲屬性設置一個合適的初始值,如果不是在定義時初始化值,可以在構造函數中賦值
  • 構造函數就像一個沒有形式參數的實例方法,使用 init 關鍵字來寫
init() {
    // perform some initialization here
}
class Person: NSObject {
    var name : String
    var age : Int

    // 重寫了NSObject(父類)的構造方法
    override init() {
        name = ""
        age = 0
    }
}

// 創建一個Person對象
let p = Person()
自定義構造函數
  • 很多時候,我們在創建一個對象時就會給屬性賦值
  • 可以自定義構造函數
  • 注意:如果自定義了構造函數,會覆蓋init()方法.即不在有默認的構造函數
class Person: NSObject {
    var name : String
    var age : Int

    // 自定義構造函數,會覆蓋init()函數
    init(name : String, age : Int) {
        self.name = name
        self.age = age
    }
}

// 創建一個Person對象
let p = Person(name: "why", age: 18)

結構體類型的成員構造函數

  • 如果結構體類型中沒有定義任何自定義構造函數,它會自動獲得一個成員構造函數。不同於默認構造函數,結構體會接收成員構造函數即使它的存儲屬性沒有默認值
//定義了一個名爲 Size 有兩個屬性分別是 width 和 height 的結構體,這兩個屬性通過分配默認值 0.0 ,從而被推斷爲 Double 類型
struct Size {
    var width = 0.0, height = 0.0
}
//Size 結構體自動接收一個 init(width:heght:) 構造函數
let twoByTwo = Size(width: 2.0, height: 2.0)

值類型的構造函數委託

  • 構造函數可以調用其他構造函數來執行部分實例的初始化。這個過程,就是所謂的構造函數委託。
  • 構造函數的運作,對於值類型和類類型是不同的。
  • 值類型(結構體和枚舉)不支持繼承,所以他它們的構造函數委託的過程相當簡單。
  • 注意如果爲值類型定義了自定義構造函數,就不能訪問默認構造函數或者是成員構造函數

類的繼承和初始化

  • 所有類的存儲屬性——包括從它的父類繼承的所有屬性都必須在初始化期間分配初始值。
  • Swift 爲類類型定義了兩種構造函數以確保所有的存儲屬性接收一個初始值。這些就是所謂的指定構造函數和便捷構造函數
    • 指定構造函數是類的主要構造函數。指定構造函數可以初始化所有那個類引用的屬性並且調用合適的父類構造函數來繼續這個初始化過程給父類鏈
    • 類偏向於少量指定構造函數,並且一個類通常只有一個指定構造函數
    • 每個類至少得有一個指定構造函數
    • 便捷構造函數是次要的,爲一個類支持構造函數。你可以在相同的類裏定義一個便捷構造函數來調用一個指定的構造函數作爲便捷構造函數來給指定構造函數設置默認形式參數。
//類的指定構造函數
init(parameters) {
    statements
}
//便捷構造函數有着相同的書寫方式,但是要用 convenience 修飾符放到 init 關鍵字前,用空格隔開:
convenience init(parameters) {
    statements
}

類類型的構造函數委託

  • 爲了簡化指定和便捷構造函數之間的調用關係,Swift 在構造函數之間的委託調用有下面的三個規則:
    • 規則 1——指定構造函數必須從它的直系父類調用指定構造函數。
    • 規則 2——便捷構造函數必須從相同的類裏調用另一個構造函數。
    • 規則 3——便捷構造函數最終必須調用一個指定構造函數。
  • 簡單記憶的這些規則的方法如下:
    • 指定構造函數必須總是向上委託。
    • 便捷構造函數必須總是橫向委託。

    類類型的構造函數委託

構造函數的繼承與重寫

  • 在Swift中,子類的構造函數有兩種來源,首先是自己擁有的構造函數,其次是從父類中繼承過來的構造函數。但是,比不是所有父類構造函數都能夠被子類繼承。子類繼承父類的構造函數是有條件的,遵守以下2個規則:
    • 規則1——如果子類沒有定義任何指定初始化器,它會自動繼承父類所有的指定初始化器
    • 規則2——如果子類提供了所有父類指定初始化器的實現——要麼是通過規則1繼承來的,要麼通過在定義中提供自定義實現的,那麼它自動繼承所有的父類便捷初始化器。
  • 如果一個子類中任意的構造器和父類的便利構造器一模一樣, 不算重寫
  • 一個例子
class Person {   
    var name: String!
    var weight: Double  
    init(name: String) {
        self.name = name
        self.weight = 0.0
    } 
    // 定義指定構造器
    init(name: String, weight: Double) {
        self.name = name
        self.weight = weight
    }
     
    // 定義便利構造器(使用convenience修飾)
    convenience init(n name: String, w weight: Double) {
        // 便利構造器必須調用同類中的指定構造器
        self.init(name: name, weight: weight)
    }
     
    convenience init(showStr: String) {
        self.init(name: "", weight: 0.0)
        print(showStr)
    }
}
 
class Man: Person {
    var sex: String = "男"   
    override init(name: String) {
        // 子類的指定構造器中必須調用父類的指定構造器
        super.init(name: name)
        self.name = name
        self.weight = 0.0
    }
    override init(name: String, weight: Double) {
        super.init(name: name, weight: weight)
        self.name = name
        self.weight = weight
    }   
    // 定義指定構造器與父類的便利構造器一樣, 這裏不算重寫
    convenience init(showStr: String) {
        self.init(name: "", weight: 0.0)
        print(showStr)
    }
}
 
var manA = Man(name: "ZhangSan", weight: 62.0)
var manB = Man(showStr: "Hello Swift")

可失敗構造函數

  • 定義類、結構體或枚舉初始化時可以失敗
  • 失敗可能由以下幾種方式觸發,包括給初始化傳入無效的形式參數值,或缺少某種外部所需的資源,又或是其他阻止初始化的情況
  • 爲了處理這種可能,在類、結構體或枚舉中定義一個或多個可失敗的構造函數。通過在 init 關鍵字後面添加問號init?
struct Animal {
    let species: String
    init?(species: String) {
        if species.isEmpty { return nil }
        self.species = species
    }
}
let someCreature = Animal(species: "Giraffe")
if let giraffe = someCreature {
    print("An animal was initialized with a species of \(giraffe.species)")
}

必要構造函數

  • 在類的構造函數前添加required 修飾符來表明所有該類的子類都必須實現該構造函數
  • 當子類重寫父類的必要構造函數時,必須在子類的構造函數前添加 required 修飾符以確保當其它類繼承該子類時,該構造函數同爲必要構造函數
  • 在重寫父類的必要構造函數時,不需要添加 override 修飾符
class SomeClass {
    required init() {
    }
}
class SomeSubclass: SomeClass {
    required init() {
    }
}

析構函數

  • Swift 會自動釋放不再需要的實例以釋放資源
    • Swift 通過自動引用計數(ARC)處理實例的內存管理
    • 當引用計數爲0時,系統會自動調用析構函數(不可以手動調用)
    • 通常在析構函數中釋放一些資源(如移除通知等操作)
  • 析構函數的寫法
deinit {
    // 執行析構過程
}
示例練習
class Person {
    var name : String
    var age : Int

    init(name : String, age : Int) {
        self.name = name
        self.age = age
    }

    deinit {
        print("Person-deinit")
    }
}

var p : Person? = Person(name: "why", age: 18)
p = nil
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章