Swift 内存管理 ⑮

  跟OC一样,Swift也是采用基于引用计数的ARC内存管理方案(针对堆空间),Swift中的ARC有三种引用:

  • 强引用(strong reference): 默认情况下都是强引用

  • 若引用(weak reference):通过weak定义弱引用
      必须是可选类型的var,因为实力销毁后,ARC会将自动弱引用置位nil
      ARC自动给弱引用设置为nil时,不会触发属性观察

  • 无主引用(unowned reference):通过unowned定义无主引用
      不会产生强引用,实例销毁后仍然存储着实例的内存地址(类似OC中的 unsafe_unretained)
      试图在实例销毁后访问无主引用,会产生运行时错误(野指针)

1. weak、unowned的使用限制

  • weak、unowned只能用在类实例上面
protocol Livable: AnyObject {}//协议继承AnyObject后只能被类继承
class Person {}

weak var p0 : Person?
weak var p1: AnyObject?
weak var p2: Livable?

unowned var p10 : Person?
unowned var p11: AnyObject?
unowned var p12: Livable?

2. 循环引用

  weak、unowned都能解决循环引用的问题,unowned要比weak 少一些性能消耗

  • 在生命周期中有可能变为nil的使用 weak
  • 初始化赋值之后,再也不会变为nil的使用unowned
闭包的循环引用:
  • 闭包表达式默认会对用到的外层对象产生额外的强引用(对外层对象进行returen操作)
class Person {
    var fn: (() -> ())?
    
    func run()  {
        print("person run")
    }

    deinit {
        print("person deinit")
    }
    
}
func tt()  {
    print("tt")
}
func test()  {
    var p = Person()
    p.fn = {p.run()}
}

test()

注意:这里并不会看到打印person deinit。因为 var p = Person(),Person对象p强引用fn闭包。当执行完 p.fn = {p.run()}时,闭包又强引用Person对象,造成循环引用

在闭包表达式的捕获列表声明weak或unowned引用,可解决循环引用问题
func test()  {
    var p = Person()
    p.fn = {
        [weak p] in
        p?.run()
    }
}

func test()  {
    var p = Person()
    p.fn = {
        [unowned p] in
        p.run()
    }
}
  • 如果想定义闭包属性的同时引用self,这个闭包必须是lazy的(因为在实例初始化完毕之后才能引用self
    lazy var fn: (() -> ()) = {
//        [weak self] in
//        self?.run()
        [weak weakSelf = self] in
        weakSelf?.run()
    }
  • 如果lazy属性是闭包调用的结果,那么不用考虑循环引用的问题(因为闭包调用结束后,闭包的生命周期就结束了)
class Person {
    var age: Int = 10

    lazy var getAge: Int = {
        self.age
    }()
}

3. 内存访问冲突

内存访问冲突会在两个访问满足下列条件时发生:
  1. 至少一个是写入操作
  2. 他们访问的是同一块内存
  3. 他们的访问时间重叠(比如在同一个函数中)
内存访问冲突.png

解决方案:


解决方案.png
如果下面的条件满足,就说明重叠访问结构体的属性时安全的:
  1. 你只访问实例存储属性,不是计算属性或者类属性
  2. 结构体时局部变量而非全局变量
  3. 结构体要么没有被闭包捕获要么纸杯费逃逸闭包捕获

你可能感兴趣的:(Swift 内存管理 ⑮)