如何为EIUIBarButtonItem的闭包添加弱引用避免控制器被持有?
首先得搞清楚为什么控制器无法释放:你的控制器持有了EIUIBarButtonItem实例,而按钮的handle闭包又强引用了控制器(因为你传入的controller.buttonAction是控制器的实例方法,闭包会默认捕获控制器的强引用),这样就形成了控制器 → 按钮 → 闭包 → 控制器的循环引用链,导致两者都无法被ARC回收。
下面给你两种解决方案,你可以根据需求选择:
方案一:修改闭包捕获方式(无需修改类代码)
这是最简单的处理方式,只需要在创建按钮时,显式在闭包捕获列表中用weak self弱化对控制器的引用:
let barButton = EIUIBarButtonItem(image: myUIImage, style: .plain) { [weak self] sender in self?.buttonAction(sender) }
这样闭包只会持有控制器的弱引用,当控制器被销毁时self会自动变为nil,循环引用链直接被打破。
方案二:修改EIUIBarButtonItem类代码,自动支持弱引用
如果你希望组件本身就能处理弱引用,不需要使用者手动添加捕获列表,可以调整类的设计,通过包装闭包实现自动弱引用:
class EIUIBarButtonItem: UIBarButtonItem { // 定义弱引用闭包包装类 private class WeakActionWrapper<T: AnyObject> { weak var target: T? let action: (T, UIBarButtonItem) -> Void init(target: T, action: @escaping (T, UIBarButtonItem) -> Void) { self.target = target self.action = action } } private var actionWrapper: AnyObject? // 新增支持弱引用的初始化方法 convenience init<T: AnyObject>(image: UIImage?, style: UIBarButtonItemStyle, target: T, action: @escaping (T, UIBarButtonItem) -> Void) { self.init(image: image, style: style) self.actionWrapper = WeakActionWrapper(target: target, action: action) self.target = self self.action = #selector(buttonAction(_:)) } // 保留原有闭包初始化方法(使用者仍需手动处理弱引用) typealias HandleClosure = (UIBarButtonItem) -> Void private var handle: HandleClosure? init(image: UIImage?, style: UIBarButtonItemStyle, handle: HandleClosure? = nil) { self.handle = handle super.init() self.image = image self.style = style self.target = self self.action = #selector(buttonAction(_:)) } required init?(coder: NSCoder) { super.init(coder: coder) } @objc private func buttonAction(_ sender: UIBarButtonItem) { // 调用原有闭包 handle?(sender) // 调用弱引用包装的闭包 if let wrapper = actionWrapper as? WeakActionWrapper<AnyObject>, let target = wrapper.target { wrapper.action(target, sender) } } }
使用修改后的类时,你可以这样创建按钮:
let barButton = EIUIBarButtonItem(image: myUIImage, style: .plain, target: self) { (controller, sender) in controller.buttonAction(sender) }
这里的target会被WeakActionWrapper弱引用,自动避开循环引用,使用者不需要手动处理捕获列表。
注意点
- 方案一更轻量,适合大多数日常场景,只需要使用者注意闭包的捕获方式;
- 方案二封装性更好,适合通用组件的封装,能降低使用者的出错概率。
内容的提问来源于stack exchange,提问作者EvGeniy Ilyin
相关产品推荐
相关产品推荐

