嵌套closure in iOS

703 阅读2分钟

加深理解代替单纯记忆

关于block的基本应用和底层认识,可参考《深入了解block》

本次只想弄清楚,遇到嵌套closure时,关于weak self的写法总有些不确定问题

先看一个例子

let closure: ClosureType = { [weak self] in
  let embeddedClosure: ClosureType = {
    let newSelf = self
  }
  embeddedClosure()
}
closure()
self.closure = closure

无内存泄漏(无循环引用)

  • 说明closure是在最近的scope中找被捕获变量,即embeddedClosure找到的是weak self

再来

let closure: ClosureType = { [weak self] in
  guard let self = self else { return }
  let embeddedClosure: ClosureType = {
    let newSelf = self
  }
}
    
self.closure = closure

无泄漏

为什么无泄漏呢?按照上面理论,embeddedClosure中不应该捕获的是guard let self = self else { return }这句产生的strong self么?

对,上面的理论没有问题。问题在于,closure根本没有执行,所以embeddedClosure根本没有机会初始化,也就不存在捕获self的问题了

  • 捕获变量行为发生在closure初始化阶段

那好,我们按照上面的说法修改正确,如下所示

let closure: ClosureType = { [weak self] in
  guard let self = self else { return }
  let embeddedClosure: ClosureType = {
    let newSelf = self
  }
}
closure()
self.closure = closure

结果仍然是--无泄漏

embeddedClosure确实初始化了,也捕获了strong的self。哪里出了问题?回想一下经典的循环引用范式self->block->self,我们也写一下该例子中的引用关系

  • self -> closure - - -> weak self 虚线表示弱引用
  • embeddedClosure -> self

此时有人(就是我)会觉得,不对啊,closure对embeddedClosure不也应该有强引用么?

该例中,embeddedClosure只是closure中的一个局部变量,closure根本没有捕获embeddedClosure,所以也就不存在任何引用了

最后

var embeddedClosure: ClosureType?
let closure: ClosureType = { [weak self] in
  guard let self = self else { return }
  embeddedClosure = {
    let newSelf = self
  }
}
closure()
self.closure = closure

终于内存泄漏了

self -> closure -> embeddedClosure -> self

总了个结

  • closure是在最近的scope中找被捕获变量
  • 捕获变量行为发生在closure初始化阶段

参考