Swift闭包Capture List疑问:weak/unowned self原理及捕获机制解惑
Hey,我太懂你这种困惑了——当年第一次啃Swift闭包的捕获列表时,我对着weak和unowned挠了好半天头,总觉得底层逻辑雾蒙蒙的。咱们一步步把这些点掰扯清楚:
Swift闭包捕获列表:从「捕获」到底层原理的清晰拆解
先搞懂「捕获」到底是什么?
首先,闭包是个引用类型,它最大的特点就是能访问定义它的上下文里的变量/常量——这个“把外部变量拉进自己作用域,并且保持关联”的动作,就是咱们说的「捕获」。
举个例子:你在ViewController里写了个闭包,里面用到了self.view,那这个闭包就捕获了self。默认情况下,闭包会对捕获的对象做强引用——也就是把对象的引用计数+1,这也是循环引用的根源(比如self持有闭包,闭包又持有self,俩都没法释放)。
weak self 和 unowned self 底层到底咋工作?
这俩都是用来改变闭包对self的引用类型,本质是控制引用计数:
- weak self:闭包对self持有的是弱引用,底层就是个可选类型的指针。当self被系统释放时,这个指针会自动变成
nil。所以用weak self的时候,你必须先解包(比如guard let self = self else { return }),因为它随时可能变成空的。这种方式最安全,适合不确定self生命周期是否比闭包长的场景。 - unowned self:闭包对self持有的是无主引用,底层是个非可选的指针。它的核心假设是:self的生命周期一定比闭包长,所以不会自动变成
nil。但如果self提前被释放了,再用unowned self就会直接触发崩溃(野指针访问)。这种适合你能100%确定闭包执行时self肯定存在的场景,比如同步执行的闭包(比如UIView.animate的完成闭包,只要当前VC没被释放,闭包执行时self一定在)。
闭包怎么在不持有对象的情况下用self?
其实就是通过捕获列表告诉闭包:“别对self做强引用,换个引用类型”。
比如把默认的强引用改成弱引用或者无主引用,这样闭包就不会增加self的引用计数,也就不会造成循环引用。
看个对比例子:
// 坑:强引用导致循环引用 class ViewController: UIViewController { // self持有这个闭包 var completion: (() -> Void)? func setupBad() { completion = { // 闭包强引用self,循环引用诞生! self.view.backgroundColor = .red } } } // 正确:用weak self避免循环引用 func setupGood() { completion = { [weak self] in // 先解包,确保self还活着 guard let self = self else { return } self.view.backgroundColor = .red // 这里的self是解包后的临时强引用,只在闭包执行期间有效,不会造成循环 } }
纠正你之前的误解:不是复制值类型那种栈传递
你之前以为是复制对象、像值类型一样在栈里传递,这个理解确实偏了。闭包是引用类型,它捕获的是对象的引用,不是复制对象本身。默认的强引用是让闭包持有这个引用的强引用(引用计数+1),而weak/unowned只是改变这个引用的类型,完全不是复制对象那回事。值类型的复制是完全独立的副本,而闭包捕获的引用是和原对象挂钩的,这本质上是两码事。
内容的提问来源于stack exchange,提问作者r.pul
相关产品推荐
相关产品推荐

