Swift使用闭包时出现的内存泄漏 (类比OC,大同小异)

1,495 阅读1分钟

记录一个最近遇到的循环引用导致的内存泄漏

  • 小样儿 你换了个语法我差点不认识你了,害我找老半天
  1. 骚话不多说,让我们来看一段代码把
//代码的第一部分 这里是一个自定义的UIView,view上就一个按钮,这个view有一个allSelectCallback 回掉,用来给外部调用的时候使用
class SomeView: UIView {
    var allSelectCallback: (() -> Void)?
    override init(frame: CGRect) {
        super.init(frame: frame)
        let btn = UIButton.init(frame: bounds)
        addSubview(btn)
        btn.addTarget(self, action: #selector(callBack), for: .touchUpInside)

    }
    @objc func callBack() {
        allSelectCallback?()
    }
}
  1. 下面是使用上面这个UIView的代码

class Controller2: UIViewController {
    lazy var leakView:SomeView = {
        //创建上面这个View
        let  view1 = SomeView.init(frame: CGRect.init(x: 100, y: 100, width: 100, height: 100))
        //view 里面的按钮点击的时候让view隐藏
        view1.allSelectCallback = { [weak self] in
            view1.isHidden = true
            self?.dismiss(animated: true, completion: nil)
        }
        return view1
    }()
    
     override func viewDidLoad() {
        
        super.viewDidLoad()
        view.addSubview(leakView)
        
    }
}
  1. 是不是看上去完全没毛病啊,甚至在回掉里面还用了weak self,哪里有内存泄漏,你丫逗我呢?
  2. 但是再仔细看看懒加载中的代码
  • view 持有一个allSelectCallback属性

  • 而在这个allSelectCallback的回掉中,调用了view.isHidden

  • 这个闭包对view的引用进行了捕获,从而对view进行了持有,所以出现了引用循环

  • 和以前用oc的时候的解决方案一样,在外面 weak var weakview = view 就可以解决问题了

  • 代码中的 [weak self] 仅仅是弱引用了controller的view ,所以controller 可以正常释放,真正没有释放掉的是 SomeView,泄漏的点在这里