望其項背 iOS - swift: 其他:通過 some 修飾不透明型別(opaque type),大 Self 和小 self,inout 引數的訪問衝突問題,引用計數器,強引用,weak 弱引用,unowned 弱引用,例項之間的互相強引用導致的無法釋放的問題,屬性閉包引用了 self 導致的迴圈引用問題
阿新 • • 發佈:2021-06-29
望其項背 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") } }