Swift 5.1 (14) - 初始化和反初始化

級別: ★☆☆☆☆
標籤:「iOS」「Swift 」「便利初始化」「初始化」「反初始化」
做者: 沐靈洛
審校: QiShare團隊php


初始化Initialization

初始化是準備類,結構體或枚舉類型實例的過程。該過程當中涉及:設置存儲屬性初始值,初始化實例所需的配置項。git

爲存儲屬性設置初始值

由於在建立類或結構體的實例後,類或結構體的全部存儲屬性必需要要有初始值,故,在類和結構體定義時就必須爲其全部存儲屬性設置適當的初始值。存儲屬性不能保留在不肯定的狀態(無初始值的狀態),不然編譯器會提示咱們:Class '*' has no initializersgithub

  1. 初始化方法中爲存儲屬性設置初始值。
class Initializers {
    //! 屬性聲明時就設置屬性的默認值
    var storeProperty : String = "變量存儲屬性聲明時就設置屬性的默認值"
    let constantStoreProperty : String = "常量存儲屬性聲明時就設置屬性的默認值"
    //! 屬性聲明爲可選類型,可選類型的屬性將自動初始化爲nil
    var optionalProperty : Array<Int>?
}
複製代碼
  1. 屬性聲明時指定默認屬性值爲其初始值。
class Initializers {
    var storeProperty : String
    let constantStoreProperty : String
    //! 屬性聲明爲可選類型,可選類型的屬性將自動初始化爲nil。能夠在初始化方法中設置其餘值,也能夠無論,看須要。
    var optionalProperty : Array<Int>?
    init() {
        storeProperty = "在初始化方法中設置了存儲屬性的初始值"
        constantStoreProperty = "在初始化方法中設置了常量存儲屬性的初始值"
    }
}
複製代碼

####自定義初始化編程

class Initializers {
    var storeProperty : String
    let constantStoreProperty : String
    var optionalProperty : Array<Int>?
    // 無參數,無標籤
    init() {
        storeProperty = "在初始化方法中設置存儲屬性的初始值"
        constantStoreProperty = "在初始化方法中設置常量存儲屬性的初始值"
    }
    //!有參數,有參數標籤的自定義初始化方法
    init(prefixed prefix:String) {
        storeProperty = prefix + "在初始化方法中設置存儲屬性的初始值"
        constantStoreProperty = prefix + "在初始化方法中設置常量存儲屬性的初始值"
    }
    //!有參數,無參數標籤的自定義初始化方法
    init(_ prefix:String) {
        storeProperty = prefix + "在初始化方法中設置存儲屬性的初始值"
        constantStoreProperty = prefix + "在初始化方法中設置常量存儲屬性的初始值"
    }
    //!多參數,有標籤
    init(prefixed prefix:String, suffixed suffix:String) {
        storeProperty = prefix + "在初始化方法中設置存儲屬性的初始值" + suffix
        constantStoreProperty = prefix + "在初始化方法中設置常量存儲屬性的初始值" + suffix
    }
    init(prefix:String,suffix:String) {
        storeProperty = prefix + "在初始化方法中設置存儲屬性的初始值" + suffix
        constantStoreProperty = prefix + "在初始化方法中設置常量存儲屬性的初始值" + suffix
    }
    //! 多參數,無標籤
    init(_ prefix:String, _ suffix:String) {
        storeProperty = prefix + "在初始化方法中設置存儲屬性的初始值" + suffix
        constantStoreProperty = prefix + "在初始化方法中設置常量存儲屬性的初始值" + suffix
    }
    class func usage(){
        //!調用:有參數,有參數標籤的自定義初始化方法
        let obj = Initializers("QiShare")
        print(obj.storeProperty + "\n" + obj.constantStoreProperty)
        //!調用:有參數,無參數標籤的自定義初始化方法
        let obj1 = Initializers(prefixed: "hasArgumentLabels")
        print(obj1.storeProperty + "\n" + obj1.constantStoreProperty)
        //!調用:多參數,有參數標籤的自定義初始化方法
        let obj2 = Initializers(prefixed: "QiShare", suffixed: "end")
        print(obj2.storeProperty + "\n" + obj2.constantStoreProperty)
        //!調用:多參數,無參數標籤的自定義初始化方法
        let obj3 = Initializers("Qishare","end")
        print(obj3.storeProperty + "\n" + obj3.constantStoreProperty)
    }
}
複製代碼

默認初始化方法

Swift 能夠爲任何結構體和類提供默認的初始化方法,前提是結構體或類中的全部屬性都被賦了初始值,而且結構體和類也沒有提供任何初始化方法。swift

結構體類型的成員初始化方法安全

若是結構類型沒有定義任何自定義的初始化方法,它們會自動生成接收成員屬性初始值的初始化方法。與結構體默認初始化方法不一樣,即便結構體的存儲屬性沒有默認值,結構體類型也會生成接收成員屬性初始值的初始化方法。bash

struct Size {
    var width = 0.0
    var height : Double
}
//! 使用
let size1 = Size.init(width: 3.0, height: 3.0)
let size2 = Size(height: 3.0)
print(size1)
複製代碼

注意:值類型中自定義了初始化方法,則將沒法再訪問該類型的默認初始化方法,或結構體自動生成的接收成員屬性初始值的初始化方法。此約束爲了保證自定義的初始化方法的優先級。微信

//自定義初始化方法
struct Size {
    var width = 0.0
    var height : Double
    init(wid:Double,hei:Double) {
        width = wid
        height = hei
    }
}
//原始生成的初始化方法失效
let size1 = Size.init(width: 3.0, height: 3.0) //< 報錯
複製代碼

值類型初始化方法嵌套調用Initializer Delegation

Initializer Delegation:值類型初始化方法中調用其餘初始化方法來執行實例初始化的一部分。 做用:避免跨多個初始化方法時出現重複代碼。 注意:初始化方法的嵌套調用在值類型和類類型中規則不一樣。值類型(結構體和枚舉)不涉及繼承,相對簡單。類類型涉及繼承,咱們須要額外的操做,來保證明例對象初始化的正確性。閉包

struct Rect {
    var origin = Point()
    var size = Size()
    init() {}
    init(origin: Point, size: Size) {
        self.origin = origin
        self.size = size
    }
    init(center: Point, size: Size) {
        let originX = center.x - (size.width / 2)
        let originY = center.y - (size.height / 2)
        self.init(origin: Point(x: originX, y: originY), size: size)
    }
}
複製代碼

類繼承和初始化

類的全部存儲屬性,包括類從其父類繼承的任何屬性,都必須在初始化期間分配初始值。Swift爲類類型定義了兩種初始化方法:指定初始化方法和便利初始化方法,來幫助確保類的全部存儲屬性都能設置初始值。ide

指定初始化方法和便利初始化方法

指定初始化方法:是類的主要初始化方法,負責徹底初始化類的全部屬性,且會調用super引入父類適當的初始化方法。 便利初始化方法:是類次要的初始化方法,便利初始化方法能夠定義默認值做爲初始化方法調用的參數值。方便咱們使用給定的默認值建立一個實例對象。

指定和便利初始化方法的語法

指定初始化方法:

init(`parameters`) {
}
複製代碼

便利初始化方法:使用convenience關鍵字來指明

convenience init(`parameters`) {
    //statements
}
複製代碼

類類型初始化方法嵌套調用Initializer Delegation

爲了簡化初始化方法與便利初始化方法之間的關係,Swift對於Initializer Delegation制定了三個規則:

  • 指定初始化方法必須調用其直接父類的指定初始化方法。
  • 便利初始化方法中必須調用同類的另外一個初始化方法 。
  • 便利初始化方法最終必須調用到指定的初始化方法。
class Animal {
    var name : String
    var kind : String = "Unknown"
    init(name:String = "[UnNamed]") {
        self.name = name
    }
    convenience init(name : String, kind : String) {
       /*便利初始化方法中必須調用同類的另外一個初始化方法
         便利初始化方法最終必須調用到指定的初始化方法
       */
        self.init(name: name)
        self.kind = kind
    }
}
class Dog: Animal {
    var nickName : String = "nihao"
    init(name : String, kind : String) {
        //指定初始化方法中,不能調用父類的便利初始化方法
        //super.init(name: name, kind: kind) // error:Must call a designated initializer of the superclass 'Animal'
        super.init(name: name)
    }
}

複製代碼

總結:指定初始化方法必須始終向上使用super代理父級的初始化。便利初始化方法必須始終橫向使用self代理同類的初始化。

`Initializer Delegation`三個規則闡述圖.png

注意:這些規則不會影響類建立實例時的使用方式。上圖中的任何初始值方法都能用於建立徹底初始化的實例。規則僅影響類初始方法的實現方式。

下圖會經過多類的複雜繼承,來闡述指定初始化方法如何在類的初始化過程當中扮演煙囪、漏斗的角色。

多類的複雜繼承關係圖.png

初始化的兩個階段

  • 第一階段,設置全部存儲屬性的初始狀態,賦初值。
  • 第二階段,有機會進一步定製其存儲屬性。

Swift的編譯器爲確保完成這兩個階段的初始化而沒有錯誤,會執行如下四個安全檢查:

  • 指定初始化方法必須確保使用super向上代理父級初始化方法以前,完成初始化本類的全部屬性。
  • 指定初始化方法中爲繼承屬性賦值以前,必須使用super向上代理父級初始化方法。不然繼承屬性的新值會因調用父級super初始化方法而覆蓋。
  • 便利初始化方法中爲任何屬性賦值以前,必須先調用同類的指定初始化方法。不然會被覆蓋。
  • 初始化的第一階段完成以前,不能在初始化方法中調用本類的任何實例方法,讀取來自父類的任何實例屬性。即:不能引用self做爲本類的一個實例對象。理解起來比較抽象,舉個例子來闡述,示例以下:
class BaseClass {
    var property1 : String = "defaultvalue"
    var property2 : String
    init() {
        property1 = "property1"
        property2 = "property2"
    }
}
class SubClass: BaseClass {
    var property3 : String
    override init() {
        //property3 = "property3"
        //super.init()
        someInstanceMethod(property2)
        /*
         報錯信息爲:
         1.'self' used in method call 'someInstanceMethod' before 'super.init' call
         2.'self' used in property access 'property2' before 'super.init' call
         */
        someInstanceMethod(property3)
        /*
         報錯信息爲:
         1.'self' used in method call 'someInstanceMethod' before 'super.init' call
         2.Variable 'self.property3' used before being initialized
         */
    }
    func someInstanceMethod(_ : String) -> Void {
    }
}
複製代碼

在第一階段結束以前,類實例不徹底有效。

基於以上四個安全檢查,以上兩個階段分別發揮的的做用總結以下:

第一階段:

  1. 類的指定初始化方法或便利初始化方法會被調用。
  2. 爲該類的新實例分配內存。內存還沒有初始化。
  3. 類的指定初始化方法確認類中全部存儲屬性都具備值。至此全部存儲屬性的內存初始化完成。
  4. 類的指定初始化方法經過super委託父類的初始化方法完成父類全部存儲屬性的初始化。
  5. 類沿着繼承鏈繼續執行與4中相同的操做,直到繼承鏈的頂端。
  6. 繼承鏈中最頂端的類在確保其全部存儲屬性都有值時,意味着實例的內存已經徹底初始化。至此第一階段完成。

第二階段:

  1. 初始化方法中能夠訪問self並能夠修改其屬性,調用實例方法等。
  2. 便利初始化方法中能夠選擇自定義實例而且使用self
class convenienceClass: Initializers {
    /* 初始化與指定初始化之間的關係
     必須調用父類的指定初始化方法
     便利初始化方法只能使用`self.init`代理類的初始化而不是使用`super.init`
     便利初始化方法必須最終調用到同類的指定初始化方法*/
    var subClassStoreProperty : String
    override init(prefixed prefix:String) {
        subClassStoreProperty = "子類的屬性"
        super.init(prefix)
        storeProperty = prefix + "在初始化方法中設置存儲屬性的初始值"
    }
    //父類中有相應的初始化方法 須要`override`
    convenience override init(_ name : String = "子類便利初始化前綴",_ suffix : String = "子類便利初始化後綴") {
        self.init(prefixed: name) //!< 必須是同類的初始化方法
        self.storeProperty = "子類的便利初始化中的存儲屬性從新賦值"
    }
}
複製代碼

初始化方法繼承和重寫

子類對父類初始化方法的覆蓋或重寫:子類提供了與父類相同的初始化方法。必需要使用override修飾。 注意:即便是子類將父類的指定初始化方法實現爲便利初始化方法也須要使用override修飾。

關於繼承:與Objective-C不一樣,Swift中子類默認狀況下是不會繼承父類的初始化方法的。只有在某些特定的狀況下才會繼承。

class Animal {
    var name : String
    init(name:String = "[UnNamed]") {
        self.name = name
    }
}
class Dog: Animal {
    var nickName : String
    init(others:String) {
        self.nickName = others
    }
}
//調用父類的初始化方法會報錯。
//let dog = Dog.init(name:"nihao")
let dog = Dog.init(others:"nihao")
print(dog.name) // 打印[UnNamed]
複製代碼

注意:關於隱式調用父類的初始化方法,即省略super.init():當父類的指定初始化方法爲零參數時,子類在其初始化方法中對其存儲屬性賦初值後,能夠省略super.init()

class Animal {
    var name : String = "defaultValue"
  //如下參數賦初值也是符合`零參數`規則,調用時也能夠對該參數進行忽略。該方法不寫也是對的。
    init(name:String = "[UnNamed]") {
        self.name = name
    }
}
class Dog: Animal {
    var nickName : String
    init(others:String) {
        self.nickName = others
        //super.init(),此處能夠省略
    }
}
//調用
let animals = Dog.init(others: "狗")
print(animals.name) // [UnNamed]
複製代碼

自動繼承初始化方法

自動繼承初始化方法意味着不須要override進行重寫。 前提:子類引入的新屬性都確保提供了默認值。 基於前提需遵照兩個規則:

  • 規則一:若是子類沒有定義任何指定的初始化方法,則子類將自動繼承其父類中全部的指定初始化方法,也會繼承便利初始化方法。
class Animal {
    var name : String = "defaultValue"
    var kind : String = "Unknown"
    init(name:String = "[UnNamed]") {
        self.name = name
    }
    convenience init(name : String, kind : String) {
        self.init(name: name)
        self.kind = kind
    }
}
class Dog: Animal {
    var nickName : String = "defaultValue"
}
//子類使用繼承自父類的便利初始化方法
let animals = Dog.init(name: "狗", kind: "狗類")
//子類使用繼承自父類的指定初始化方法
let animals1 = Dog.init(name: "狗")
print(animals.name,animals1.kind)//!< 狗 Unknown
複製代碼
  • 規則二:若是子類提供了全部父類指定初始化方法的實現。則子類會自動繼承全部父類的便利初始化方法。在子類對於父類全部指定初始化方法的實現中,加入咱們自定義的部分也是能夠的。
class Animal {
    var name : String = "defaultValue"
    var kind : String = "Unknown"
    init(name:String = "[UnNamed]") {
        self.name = name
    }
    init(name2:String) {
        self.name = name2
    }
    convenience init(name : String, kind : String) {
        self.init(name: name)
        self.kind = kind
    }
}
class Dog: Animal {
    var nickName : String
    override init(name:String = "[UnNamed]") {
        self.nickName = "默認暱稱"
        super.init(name: name)
        self.name = "自定義實現爲:哈士奇"
    }
    override init(name2:String) {
        self.nickName = "默認暱稱"
        super.init(name2: name2)
        self.name = "自定義實現爲:哈士奇2"
    }
}
//子類使用繼承自父類的便利初始化方法
let animals = Dog.init(name: "狗", kind: "狗類")
//子類使用繼承自父類的指定初始化方法
let animals1 = Dog.init(name: "狗")
print(animals.name,animals1.kind)//!< 自定義實現爲:哈士奇 Unknown
複製代碼

注意點:子類能夠將父類指定的初始化方法實現爲子類便利初始化方法,也是知足規則二的。

class Animal {
    var name : String = "defaultValue"
    var kind : String = "Unknown"
    init(name:String = "[UnNamed]") {
        self.name = name
    }
    init(name2:String) {
        self.name = name2
    }
    convenience init(name : String, kind : String) {
        self.init(name: name)
        self.kind = kind
    }
}
class Dog: Animal {
    var nickName : String
    convenience override init(name:String = "[UnNamed]") {
        self.init(name2: name)
        self.nickName = "默認暱稱"
        self.name = "自定義實現爲:哈士奇"
    }
    override init(name2:String) {
        self.nickName = "默認暱稱"
        super.init(name2: name2)
        self.name = "自定義實現爲:哈士奇2"
    }
}
//子類使用繼承自父類的便利初始化方法
let animals = Dog.init(name: "狗", kind: "狗類")
//子類使用繼承自父類的指定初始化方法
let animals1 = Dog.init(name: "狗")
print(animals.name,animals1.kind)//!< 自定義實現爲:哈士奇 Unknown
複製代碼

實操中的指定和便利初始化方法

如下示例定義了三個類,分別爲FoodRecipeIngredientShoppingListItem它們之間爲繼承關係。將用來展現指定初始化方法、便利初始化方法以及自動繼承初始化方法之間的相互做用。

class Food {
    var name: String
    init(name: String) {
        self.name = name
    }
    convenience init() {
        self.init(name: "[Unnamed]")
    }
}

class RecipeIngredient: Food {
    var quantity: Int
    init(name: String, quantity: Int) {
        self.quantity = quantity
        super.init(name: name)
    }
    override convenience init(name: String) {
        self.init(name: name, quantity: 1)
    }
}

class ShoppingListItem: RecipeIngredient {
    var purchased = false
    var description: String {
        var output = "\(quantity) x \(name)"
        output += purchased ? " ✔" : " ✘"
        return output
    }
}
複製代碼

初始化方法之間的做用解釋以下:

Food爲基類,定義了一個指定初始化方法init(name: String)和一個便利初始化方法convenience init()

RecipeIngredient繼承自Food

  1. 定義了屬於本身的初始化方法:init(name: String, quantity: Int)並在其中代理實現了父類的初始化super.init(name: name)。此處符合初始化的第一階段:設置全部存儲屬性的初始狀態,賦初值。
  2. 將父類的指定初始化方法實現爲便利初始化方法:override convenience init(name: String)。該類在初始化的過程當中,能夠確保自增的屬性quantity有初值;而且init(name: String)爲父類Food的惟一指定初始化方法。這點知足了自動繼承初始化方法的規則二:若是子類提供了全部父類指定初始化方法的實現,則子類會自動繼承全部父類的便利初始化方法。故該類繼承了父類的便利初始化方法convenience init()

ShoppingListItem繼承自RecipeIngredient,未提供任何指定的初始化方法,此處知足了自動繼承初始化方法的規則 一:若是子類沒有定義任何指定的初始化方法,則子類將自動繼承其父類中全部的指定初始化方法,也會繼承便利初始化方法。

因此構建RecipeIngredientShoppingListItem的實例能夠經過如下三種方式:

RecipeIngredient(),
RecipeIngredient(name: "醋"),
RecipeIngredient(name: "大蔥", quantity: 6)
//`ShoppingListItem`的實例
ShoppingListItem(),
ShoppingListItem(name: "姜"),
ShoppingListItem(name: "雞蛋", quantity: 6)
複製代碼

以上闡述能夠用一張圖來展現:

初始化方法關係分析.png

可失敗的初始化方法

使用init?來聲明一個classstructureenumeration可失敗的初始化方法。這種失敗可能會被無效的初始化參數,必要資源的缺乏等阻礙初始化成功的條件觸發。

須要注意的是:

  1. 不能使用相同的參數類型和名稱既定義可失敗的又定義不可失敗的初始化方法。
  2. 可失敗的初始化方法將建立該類型的可選值,經過在可失敗的初始化方法中return nil來表示初始化失敗的條件被觸發了。
class Animal {
    var name : String 
    init?(name:String) {
        if name.isEmpty {
            return nil
        }
        self.name = name
    }
}
if let _  = Animal.init(name: "") {
    print("初始化成功")
} else {
    print("初始化失敗")//!< 輸出
}
複製代碼

枚舉類型可失敗的初始化方法

enum ResponseStatus {
    case ResponseStatus(Int,String)
    case ResponseFail
    case ResponseSuccess
    init?(code: Int,des:String){
        switch code {
        case 401:
             self = .ResponseStatus(code,des)
        case 500:
            self = .ResponseFail
        case 200:
            self = .ResponseSuccess
        default:
            return nil
        }
    }
}
//調用
if let status = ResponseStatus.init(code: 401, des: "未受權") {
    switch status {
    case .ResponseStatus(let code, let status):
        print(code,status) //401 未受權
    default:
        print("失敗了")
    }
}
複製代碼

具備原始值的枚舉的可失敗的初始化方法

enum Direction: Int {
    case east = 0,west = 1, south = 2, north = 3
}
//調用
if let dir = Direction.init(rawValue: 2) {
    print(dir.self) //!< south
} else {
    print("失敗了")
}
複製代碼

初始化失敗的傳播

值類型,類類型均可以使用初始化方法的委託,能夠是同類委託,也能夠是子類委託父類,不論是何種方式,若咱們委託的初始化方法形成了初始化失敗,整個初始化的過程會當即失敗,不會再繼續執行。 注意:可失敗的初始化方法也能夠委託一個不可失敗的初始化方法,使用這種方式,咱們須要在初始化的過程當中添加潛在的失敗操做,不然將不會失敗。

class Animal {
    var name : String = "defaultValue"
    init(name:String) { //加問號也行
        self.name = name
    }
}
class Dog: Animal {
    var kind : String
    init?(name : String, kind : String) {
        if kind.isEmpty {
            return nil
        }
        self.kind = kind
        super.init(name: name)
    }
}
//調用
if let _ = Dog.init(name: "", kind: "") {
    print("初始化成功")
} else {
    print("初始化失敗")//!< 輸出
}
複製代碼

重寫可失敗的初始化方法

子類能夠重寫父類可失敗的初始化方法。子類也能夠將其重寫爲不可失敗的初始化方法來使用:意味着父類中此方法可fail,重寫後便不可fail

注意:

  1. 能夠將可失敗的初始化方法覆蓋爲不可失敗的初始化方法,但不能反過來。
  2. 將可失敗的初始化方法覆蓋爲不可失敗的初始化方法時,委託父類可失敗的初始化方法時,須要強制解包可用的父類初始化結果。
class Animal {
    var animalName : String = "defaultValue"
    init?(name:String) {
        if name.isEmpty { return nil }
        animalName = name
    }
}
class Dog: Animal {
    var kind : String
    override init(name:String) {
        kind = "defaultKind"
        //處理父類可能初始化失敗的狀況
        if name.isEmpty {
            super.init(name: "[UnKnown]")!
        } else {
            super.init(name: name)!
        }
    }
}
//調用
let dog = Dog.init(name: "")
print(dog.animalName)
複製代碼

init!可失敗的初始化方法

定義一個可失敗的初始化程序,經過關鍵字init?。當定義一個可失敗的初始化方法,用於建立相應類型的隱式解包的可選實例時,經過關鍵字init!。 能夠在init!中委託init?,反之亦然,能夠重寫init?init!反之亦然;也能夠在init中委託init!,這樣作,在init!初始化失敗時,會觸發斷言。

必需的初始化方法

使用required關鍵字,來表示每一個子類都必須實現該初始化方法。

class SomeClass {
    required init() {
    }
}
複製代碼

子類在實現必需的初始化方法時,也必須使用required關鍵字來表示這個必需的初始化方法也適用於繼承鏈中的其餘子類。

class SomeSubclass: SomeClass {
    required init() {
        //super.init()
    }
}
複製代碼

注意: 若知足初始化方法的繼承條件,則沒必要顯式實現required修飾的初始化方法。

使用閉包或函數設置默認屬性值

若是存儲屬性的默認值須要某些自定義或設置,則可使用閉包或全局函數爲該屬性提供自定義的默認值。每當初始化屬性所屬類型的新實例時,將調用閉包或函數,並將其返回值指定爲屬性的默認值。

使用閉包賦值的形式,函數與之相似:

class SomeClass {
    let someProperty: SomeType = {
        // `someValue` 必須是` SomeType`類型
        return someValue
    }()
}
複製代碼

注意: 若是使用閉包來初始化屬性,須要注意在執行閉包時還沒有初始化實例的其他部分。意味着沒法從閉包中訪問任何其餘屬性值,即便這些屬性具備默認值也是如此。也不能使用隱式self屬性和調用任何實例的方法。

反初始化Deinitialization

Deinitialization能夠理解爲對象析構函數與對象初始化對應,在釋放對象內存以前調用。當對象結束其生命週期,調用析構函數釋放內存。Swift中經過自動引用計數處理實例的內存管理。 注意:

  1. 析構函數在實例釋放內存以前自動調用,不容許手動調用。
  2. 父類的析構函數被子類繼承,並在子類對析構函數的實現調用結束後自動調用。即便子類不提供本身的析構函數,父類的析構函數也老是會被調用。

析構函數僅在類類型中有效,使用deinit關鍵字。寫法:

deinit {
}
複製代碼

參考資料: swift 5.1官方編程指南


瞭解更多iOS及相關新技術,請關注咱們的公衆號:

image

小編微信:可加並拉入《QiShare技術交流羣》。

image

關注咱們的途徑有:
QiShare(簡書)
QiShare(掘金)
QiShare(知乎)
QiShare(GitHub)
QiShare(CocoaChina)
QiShare(StackOverflow)
QiShare(微信公衆號)

推薦文章:
淺談編譯過程
深刻理解HTTPS 淺談 GPU 及 「App渲染流程」
iOS 查看及導出項目運行日誌
Flutter Platform Channel 使用與源碼分析
開發沒切圖怎麼辦?矢量圖標(iconFont)上手指南
DarkMode、WKWebView、蘋果登陸是否必須適配?
奇舞團安卓團隊——aTaller
奇舞週刊

相關文章
相關標籤/搜索