如何为直接赋值给闭包变量的函数设置weak self,实现相同内存管理效果
解决实例方法赋值闭包导致的循环引用问题
问题原因
直接将TabController的实例方法changeTab赋值给tabView.itemTapped时,实例方法会隐式捕获当前self(即TabController实例),形成循环引用链:TabController持有TabView,TabView的itemTapped闭包持有TabController,导致两者都无法被ARC自动释放。
实现示例1写法的内存安全版本
要保持直接赋值方法的简洁性,同时避免循环引用,可以通过自定义工具方法将实例方法包装为弱引用闭包:
步骤1:添加泛型工具方法
写一个全局泛型函数,用来将实例方法转换为带weak self的闭包:
func weaklyCaptured<T: AnyObject, Args>(_ instance: T, method: @escaping (T) -> (Args) -> Void) -> (Args) -> Void { return { [weak instance] args in guard let instance = instance else { return } method(instance)(args) } }
步骤2:修改示例1的赋值方式
在setupViewActions中,用这个工具方法包装changeTab方法:
func setupViewActions() { // 此写法和示例2一样能正常释放TabController tabView.itemTapped = weaklyCaptured(self, method: TabController.changeTab) }
完整修正后的代码
class TabController: UITabBarController { var tabView: TabView = TabView() // 假设已完成初始化 func setupViewActions() { // 修正后的无循环引用写法 tabView.itemTapped = weaklyCaptured(self, method: TabController.changeTab) // 原示例2的安全写法(对比参考) tabView.itemTapped = { [weak self] tab in self?.changeTab(tab: tab) } } func changeTab(tab: TabItem) { self.selectedIndex = tab.index } } enum TabItem { case home, profile var index: Int { // 修正Swift类型名大小写 switch self { case .home: return 0 case .profile: return 1 } } } class TabView: UIView { var itemTapped: ((_ tab: TabItem) -> Void)? } // 全局泛型工具方法 func weaklyCaptured<T: AnyObject, Args>(_ instance: T, method: @escaping (T) -> (Args) -> Void) -> (Args) -> Void { return { [weak instance] args in guard let instance = instance else { return } method(instance)(args) } }
原理说明
- 工具方法
weaklyCaptured接收实例和对应的类方法引用(TabController.changeTab是类级别的方法引用,不会直接捕获实例),返回一个新闭包。 - 返回的闭包通过
[weak instance]弱引用捕获原实例,当实例被释放时,闭包内的instance会变为nil,自动终止方法调用,从而打破循环引用。
内容的提问来源于stack exchange,提问作者Gorthez
相关产品推荐
相关产品推荐

