MVVM用闭包绑定View与ViewModel时的循环引用原因及[weak self]作用
问题解答
强引用环的产生逻辑
首先明确该场景下的对象持有关系:
ProductListVC(即代码中的self)强持有viewModel存储属性ProductViewModel强持有reloadTableViewClosure闭包(闭包为Swift引用类型,赋值给类的存储属性时默认是强引用)- 闭包默认会强捕获内部引用的外部实例对象:你在闭包内部使用了
self.productTblView,因此闭包会自动强持有self
最终形成持有闭环:
ProductListVC→ 强持有 →ProductViewModel→ 强持有 →reloadTableViewClosure→ 强持有 →ProductListVC
闭环内所有对象的引用计数都无法降到0,就算你销毁ProductListVC(比如pop出导航栈),三者都会一直占用内存,造成内存泄露。
使用[weak self]的必要性
[weak self]是闭包的捕获列表声明,作用是告诉闭包:不要强持有捕获的self实例,改用弱引用。
加入[weak self]后,闭包对self的引用为弱引用,上述持有闭环直接断裂:当ProductListVC被销毁时,它对viewModel的强引用消失,viewModel和其持有的闭包也会随之销毁,不会产生内存泄露。
另外弱引用会在指向的实例销毁后自动置为nil,就算闭包在self销毁后才执行(比如网络请求回来时页面已经关闭),self?.productTblView.reloadData()只会静默失效,不会出现野指针崩溃。
该场景下[weak self]与[unowned self]的用法区别
两者都是用来避免闭包强捕获实例的修饰符,核心差异如下:
weak:修饰的引用为可选类型,实例销毁时自动置为nil,访问时需要用?/!解包,安全性更高unowned:修饰的引用为非可选类型,实例销毁时不会自动置空,访问已销毁的实例会直接触发崩溃,性能略高于weak
该场景必须优先使用[weak self],不要用[unowned self]:因为你的viewModel.fetchData()大概率是异步网络请求,完全可能出现请求返回时用户已经关闭了ProductListVC的情况,用unowned会直接触发崩溃,weak则可以安全处理这种场景。
常见误区答疑:为什么self没有持有闭包也会产生强引用?
你产生这个困惑的核心是没有明确Swift闭包的捕获规则:闭包对外部实例的强捕获是闭包本身的行为,和你有没有直接持有这个闭包没有任何关系。只要你在闭包内部引用了外部的类实例,闭包默认就会强持有这个实例,就算你从来没有把这个闭包赋值给self的属性,只要符合前面的持有闭环逻辑,就会产生内存泄露。
内容的提问来源于stack exchange,提问作者Meri
相关产品推荐
相关产品推荐

