天天看點

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

重要說明

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

構造函數的介紹

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

預設構造函數

  • 在建立類和結構體的執行個體時必須為所有的存儲屬性設定一個合适的初始值,如果不是在定義時初始化值,可以在構造函數中指派
  • 構造函數就像一個沒有形式參數的執行個體方法,使用

    init

    關鍵字來寫
class Person {
    var name:String
    var age:Int
    var sex:String
    //1.構造函數沒有func修飾
    //2.構造函數預設完成調用 不能手動調用
    init() {      
        print("被調用")
        name = "Zhangsan"
        age = 10
        sex = "male"
    }
}
var p = Person()
p.age
p.name
p.sex
           
自定義構造函數
  • 希望在建立一個對象時手動給屬性指派(屬性的值是在外面傳進去的)
  • 可以自定義構造函數
  • 自定義構造函數和預設構造函數可以同時存在
//定義類
class Person {
    var name:String
    var age:Int
    var sex:String
    //預設構造函數
    init() {

        print("被調用")
        name = "Zhangsan"
        age = 10
        sex = "male"
    } 
    //自定義構造函數
    init(name:String, age:Int, sex:String) {        
        self.name = name
        self.age = age
        self.sex = sex   
    }
}
var p = Person()
p.age
p.name
p.sex

var p2 = Person(name: "Wangli", age: 11, sex: "female")
p2.age
p2.name
p2.sex
           

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

  • 如果結構體類型中沒有定義任何自定義構造函數,它會自動獲得一個成員構造函數
//定義了一個名為 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)
           

值類型的構造函數委托

  • 構造函數可以調用其他構造函數來執行部分執行個體的初始化。這個過程,就是所謂的

    構造函數委托

  • 構造函數委托對于值類型和類類型是不同的。
  • 值類型(結構體和枚舉)不支援繼承,是以它們的構造函數委托的過程相對簡單
  • 注意如果為值類型定義了自定義構造函數,就不能通路預設構造函數或者是成員構造函數
struct Size {
    var width = 0.0, height = 0.0
    init() {    
        //構造函數委托
        self.init(width: 2.0, height: 2.0)
    }
    init(width:Double, height:Double) {
        self.width = width
        self.height = height
    }
}
//要麼不要寫任何構造函數,要麼全寫所有的構造函數,否則下面第二種調用方式會有問題,參考上面第四條
var size = Size()
size.width
size.height
var size2 = Size(width: 1.2, height: 1.2)
size2.width
size2.height
           

類的繼承和初始化

  • 所有類的存儲屬性——包括從它的父類繼承的所有屬性都必須在初始化期間配置設定初始值。
  • Swift 為類類型定義了兩種構造函數以確定所有的存儲屬性接收一個初始值,它們就是指定構造函數(Designated Initializer)和便捷構造函數(Convenience Initializer)
    • 指定構造函數是類的主要構造函數。指定構造函數可以初始化所有類引用的屬性并且調用合适的父類構造函數來繼續這個初始化過程給父類鍊
    • 一個類通常隻有一個指定構造函數并且每個類至少得有一個指定構造函數
    • 便捷構造函數是次要的,可以在相同的類裡定義一個便捷構造函數來調用一個指定構造函數給指定構造函數設定預設形式參數
//類的指定構造函數
init(parameters) {
    statements
}
//便捷構造函數有着相同的書寫方式,但是要用 convenience 修飾符放到 init 關鍵字前,用空格隔開
convenience init(parameters) {
    statements
}
           

類類型的構造函數委托

  • 為了簡化指定和便捷構造函數之間的調用關系,Swift 在構造函數之間的委托調用有下面的三個規則:
    • 規則 1——

      指定構造函數

      必須從它的直系父類調用

      指定構造函數

    • 規則 2——

      便捷構造函數

      必須從相同的類裡調用另一個

      構造函數(可以是指定也可以是便捷)

    • 規則 3——

      便捷構造函數

      最終必須調用一個

      指定構造函數

  • 簡單記憶的這些規則的方法如下:
    • 指定構造函數必須總是向上委托。
    • 便捷構造函數必須總是橫向委托。
      19.Swift學習之構造函數與析構函數
class Car{
    var speed:Double    
    //Designated Initializer
    init(speed:Double) {
        self.speed = speed
    }
    convenience init(){
        self.init(speed: 60.0)
    }
}

class Bus : Car {    
    var wheels : Int 
    init(wheels: Int) {      
        self.wheels = wheels   
        //由于子類繼承了父類中的存儲屬性 是以必須借助父類的指定構造函數來初始化繼承的那個存儲屬性的值
        //一定要在子類的屬性初始化完畢以後調用
        super.init(speed: 120.0)
    }
    convenience init(){    
        self.init(wheels: 6)
    }
    
}
           

構造函數的繼承與重寫

  • 在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 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) {
        
        self.sex = "女"
        // 子類的指定構造函數中必須調用父類的構造函數
        // 重寫的時候,必須将調用父類的構造函數語句放在調用父類屬性的前面
        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?

  • 可失敗的構造函數裡面應該有一個

    return nil

    的語句(雖然沒有也不報錯)
  • 通過可失敗的構造函數構造出來的執行個體是一個可選型
struct Animal {
    let species: String
    init?(species: String) {
        //  傳回一個nil
        if species.isEmpty { return nil }
        self.species = species
    }
}
//傳回的類型是目前類型的可選型
let cat = Animal(species: "CAT")
if let cat = cat {   
    cat.species
}
let dog = Animal(species: "")
if let dog = dog {   
    dog.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) {
        print("Person-init")
        self.name = name
        self.age = age
    }

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

var person : Person? = Person(name: "Zhangsan", age: 18)
person = nil
           

繼續閱讀