1. 程式人生 > 其它 >望其項背 iOS - swift: 其他:通過 some 修飾不透明型別(opaque type),大 Self 和小 self,inout 引數的訪問衝突問題,引用計數器,強引用,weak 弱引用,unowned 弱引用,例項之間的互相強引用導致的無法釋放的問題,屬性閉包引用了 self 導致的迴圈引用問題

望其項背 iOS - swift: 其他:通過 some 修飾不透明型別(opaque type),大 Self 和小 self,inout 引數的訪問衝突問題,引用計數器,強引用,weak 弱引用,unowned 弱引用,例項之間的互相強引用導致的無法釋放的問題,屬性閉包引用了 self 導致的迴圈引用問題

專案地址 https://github.com/webabcd/IosDemo
作者 webabcd

望其項背 iOS - swift: 其他:通過 some 修飾不透明型別(opaque type),大 Self 和小 self,inout 引數的訪問衝突問題,引用計數器,強引用,weak 弱引用,unowned 弱引用,例項之間的互相強引用導致的無法釋放的問題,屬性閉包引用了 self 導致的迴圈引用問題

示例如下:

SwiftView15.swift

/*
 * 本例用於演示其他
 * 通過 some 修飾不透明型別(opaque type),大 Self 和小 self,inout 引數的訪問衝突問題,引用計數器,強引用,weak 弱引用,unowned 弱引用,例項之間的互相強引用導致的無法釋放的問題,屬性閉包引用了 self 導致的迴圈引用問題
 */

import SwiftUI

struct SwiftView15: View {
    
    var result: String = ""
    
    init() {
        result = sample1() // 通過 some 修飾不透明型別(opaque type)
        result += "\n"
        result += sample2() // 大 Self 和小 self
        result += "\n"
        result += sample3() // inout 引數的訪問衝突問題
        result += "\n"
        result += sample4() // 引用計數器,強引用,weak 弱引用,unowned 弱引用
        result += "\n"
        result += sample5() // 例項之間的互相強引用導致的無法釋放的問題
        result += "\n"
        result += sample6() // 屬性閉包引用了 self 導致的迴圈引用問題
    }

    var body: some View {
        VStack {
            HStack {
                Text(result)
                Spacer()
            }
            Spacer()
        }
    }
    
    func sample1() -> String {
        let a = swiftView15_func1().name
        
        return "\(a)"
    }

    func sample2() -> String {
        let a = SwiftView15_Struct2().getSelf().name
        
        return "\(a)"
    }
    
    func sample3() -> String {
        // 下面這句會導致訪問衝突,會執行時報錯 Simultaneous accesses to 0x106bc01d8, but modification requires exclusive access.
        // let a = swiftView15_func2(number: &swiftView15_var1)
        
        return ""
    }
    
    func sample4() -> String {
        var a: SwiftView15_MyClass? = SwiftView15_MyClass(name: "strong") // a 被 sample4() 強引用了,此時 a 的引用計數器是 1
        var b = a // a 被 b 強引用了,此時 a 的引用計數器是 2,b 被 sample4() 強引用了,此時 b 的引用計數器是 1
        a = nil // a 變為 nil,但是 b 不是 nil,此時 a 的引用計數器是 1
        // 此時 a 沒有被釋放,而是要等到 sample4() 執行完後,b 的引用計數器就變為 0 了,然後 a 的引用計數器也變為 0 了,然後 a 就會被釋放了
        
        weak var c: SwiftView15_MyClass? = SwiftView15_MyClass(name: "weak") // 因為 c 是 weak 弱引用,所以 c 被 sample4() 弱引用了,此時 c 的引用計數器是 0
        // 此時 c 已經被釋放了,因為 c 是 weak 弱引用,所以此時的 c 的值為 nil

        unowned var d: SwiftView15_MyClass? = SwiftView15_MyClass(name: "unowned") // 因為 d 是 unowned 弱引用,所以 d 被 sample4() 弱引用了,此時 d 的引用計數器是 0
        // 此時 d 已經被釋放了,因為 d 是 unowned 弱引用,所以如果你此時獲取 d 的值的話,將會執行時報錯 Fatal error: Attempted to read an unowned reference
        
        // 由上可知,weak 和 unowned 都是弱引用,他們的區別就是釋放後,你獲取 weak 物件的值會得到 nil,你獲取 unowned 物件的值會收到執行時異常
        
        return "\(b), \(c)"
    }

    func sample5() -> String {
        let class1_1: SwiftView15_Class1? = SwiftView15_Class1(name: "strong")
        let class2_1: SwiftView15_Class2? = SwiftView15_Class2(name: "strong")
        class1_1!.class2 = class2_1
        class2_1!.class1 = class1_1
        // 上例 class1_1 物件和 class2_1 物件互相強引用了,他們永遠都不會被釋放,除非把 app 殺了
        
        
        let class1_2: SwiftView15_Class1? = SwiftView15_Class1(name: "weak")
        let class2_2: SwiftView15_Class2? = SwiftView15_Class2(name: "weak")
        class1_2!.class2 = class2_2
        class2_2!.class1_weak = class1_2
        // 上例 class1_2 和 class2_2 會在 sample5() 執行完後被釋放,關於引用計數器,以及 weak 和 unowned 的詳細說明請參見 sample4() 中的示例
        
        
        let class1_3: SwiftView15_Class1? = SwiftView15_Class1(name: "unowned")
        let class2_3: SwiftView15_Class2? = SwiftView15_Class2(name: "unowned")
        class1_3!.class2 = class2_3
        class2_3!.class1_unowned = class1_3
        // 上例 class1_3 和 class2_3 會在 sample5() 執行完後被釋放,關於引用計數器,以及 weak 和 unowned 的詳細說明請參見 sample4() 中的示例
        
        
        return ""
    }
    
    func sample6() -> String {
        let a = SwiftView15_Class3(name: "closure_strong")
        let b = SwiftView15_Class3(name: "closure_weak")
        
        let c = a.getMessage() // 物件 a 無法釋放
        let d = b.getMessage_unowned() // 物件 b 會在 sample6() 執行完後被釋放
        
        return "\(c), \(d)"
    }
}


// 用於演示不透明型別(opaque type)
protocol SwiftView15_Protocol1 {
    associatedtype MyType
    var name: MyType { get set }
}
struct SwiftView15_Struct1: SwiftView15_Protocol1 {
    typealias MyType = String
    var name: MyType = "webabcd"
}
// 一般情況通過 -> protocol 定義返回型別是沒問題的
// 但是這裡的 SwiftView15_Protocol1 協議中定義了關聯型別,也就是說無法確認真實的返回型別
// 所以這裡如果通過 -> SwiftView15_Protocol1 定義返回型別的話,會編譯時報錯 Protocol 'SwiftView15_Protocol1' can only be used as a generic constraint because it has Self or associated type requirements
// 於是為了解決這個問題,就引入了不透明型別,即將返回型別定義為 -> some SwiftView15_Protocol1 就好了
func swiftView15_func1() -> some SwiftView15_Protocol1 { // 通過 some 修飾不透明型別
    return SwiftView15_Struct1()
}


// 用於演示大 Self 和小 self
protocol SwiftView15_Protocol2 {
    var name: String { get set }
    func getSelf() -> Self // 這裡的大 Self 指的是實現此協議的型別
}
struct SwiftView15_Struct2: SwiftView15_Protocol2 {
    var name: String = "webabcd"
    func getSelf() -> SwiftView15_Struct2 {
        return self // 這裡的小 self 指的是當前的類例項
    }
}


// 用於演示 inout 引數的訪問衝突
var swiftView15_var1 = 0
// 呼叫下面的方法時,如果傳參是 &swiftView15_var1 就會導致訪問衝突
// 因為 number 和 swiftView15_var1 引用的是相同的記憶體,且 swiftView15_var1 需要讀,number 需要寫,也就是說在同一記憶體中要同時讀寫,這樣就產生了衝突
func swiftView15_func2(number: inout Int) {
    number = swiftView15_var1
}


// 用於演示強引用,weak 弱引用,unowned 弱引用
// 在 swift 中也是通過自動引用計數器(ARC)來管理記憶體的,也就是說如果物件的被強引用的計數為 0 時就會被釋放
class SwiftView15_MyClass {
    var name: String
    init(name: String) {
        self.name = name
        print("\(self.name) SwiftView15_MyClass init")
    }
    deinit {
        print("\(self.name) SwiftView15_MyClass deinit")
    }
}


// 用於演示類例項之間的互相強引用,互相強引用會導致無法釋放
// 通過 weak 引用和 unowned 引用解決無法釋放的問題
// 注:struct 是值型別,所以沒有辦法做到下面這樣
class SwiftView15_Class1 {
    var name: String
    init(name: String) {
        self.name = name
    }
    deinit {
        print("\(self.name) SwiftView15_Class1 deinit")
    }
    var class2: SwiftView15_Class2? // class2 被 class1 強引用了
}
class SwiftView15_Class2 {
    var name: String
    init(name: String) {
        self.name = name
    }
    deinit {
        print("\(self.name) SwiftView15_Class2 deinit")
    }
    var class1: SwiftView15_Class1? // class1 被 class2 強引用了
    weak var class1_weak: SwiftView15_Class1? // class1 被 class2 通過 weak 弱引用了
    unowned var class1_unowned: SwiftView15_Class1? // class1 被 class2 通過 unowned 弱引用了
}


// 用於演示屬性閉包的迴圈引用,迴圈引用會導致無法釋放
// 通過捕獲列表並結合 weak 引用和 unowned 引用解決無法釋放的問題
class SwiftView15_Class3 {
    var name: String
    init(name: String) {
        self.name = name
    }
    
    // 注:如果要在屬性的閉包中使用 self 則需要將屬性標記為 lazy 屬性
    // 如果在屬性的閉包中引用了 self,就會導致例項持有閉包,閉包持有例項的迴圈應用,就會導致例項無法釋放
    lazy var getMessage: () -> String = {
        return self.name
    }
    
    lazy var getMessage_unowned: () -> String = {
        // 怎麼解決屬性閉包引用了 self 導致的迴圈引用問題呢,像如下方式弱引用 self 並將其新增進捕獲列表即可
        [unowned self] in
        
        // 可以類似如下這樣,弱引用 self 的同時將他賦值給另一個變數。另外,如果需要將多個引用加入捕獲列表的話用逗號隔開即可
        // [unowned xxx = self] in
        
        return self.name
    }

    deinit {
        print("\(self.name) SwiftView15_class3 deinit")
    }
}

專案地址 https://github.com/webabcd/IosDemo
作者 webabcd