望其项背 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

posted @ 2021-06-29 09:17  webabcd  阅读(39)  评论(0编辑  收藏  举报