Xcode8调试黑科技:Memory Graph实战解决闭包引用循环问题

Xcode8的调试技能又增长了一个黑科技:Memory Graph。简单的说就是能够在运行时将内存中的对象生成一张图。在现场的开发者听到了这个消息时响起了雷鸣般的掌声!咱们来看看前方记者发回的现场照片:swift

妈妈说不再用担忧引用循环啦!除非你是个瞎子。
api


那么经过一个实际项目来练习一下吧。
首先咱们写了一个自定义UIView:MyView。初始化的时候接收一个没有参数也没有返回值的闭包做为参数,并存为本身的属性:

typealias Action = () -> Void

class MyView: UIView {

    var action: Action?

    init(action: @escaping Action) {
        self.action = action
        super.init(frame: CGRect.zero)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}复制代码

接着咱们在一个ViewController中初始化MyView,而且也保存为属性:闭包

class ViewController: UIViewController {

    @IBOutlet weak var label: UILabel!

    var myView: MyView?

    override func viewDidLoad() {
        super.viewDidLoad()
        myView = MyView(action: testMethod)
    }

    func testMethod() {
        label.text = "haha"
    }

}复制代码

这vc的view上有一个label控件,在viewDidLoad时初始化myView,而且将自身的一个testMethod方法当作参数传给了myView。
testMethod中设置了自身label的text。
注意,划重点了!app


这里体现了swift函数式的特性:函数能够自由的当作一个变量传递,而且是值类型。

这个例子影射里开发中一个常见的场景:一个tableViewCell中有一个删除按钮,经过闭包将方法传进去,cell保存这个闭包;另外一方面这个闭包被调起后,删除某条数据后刷新数据源。ide

那么这么写会产生引用循环吗?函数

func testMethod() {
        label.text = "haha"
    }复制代码

核心在这段代码上,一个类的方法里设置自身的属性,会捕捉这个属性吗?这个地方能够写self,可是捕捉策略是unowned仍是strong呢?
这个闭包的实现是不能本身声明捕捉策略的:ui

因而就来验证一下。运行起来后,push这个ViewController后pop出去(记得要进行两次,好像只有一次Xcode有时不会启动分析)。
接着点击这个按钮:spa


这个时候就进入了断点模式,能够查看issue面板,注意选择右边Runtime:


有不少叹号说明就有问题了。看内存中object的名字,有一条是Closure captures leaked。展开后点击就能够看到这个issue对应的内存图形展现在中间的面板中。
固然了,咱们更多的时候是在debug页面下查看:


注意到咱们刚才的对象名:一个叫MyView,一个叫ViewController。咱们pop了两次,按理说内存里不该该有这个两个对象,然而仍是有两份实例。因此,这里面引用循环了。点击紫色的叹号会出现Xcode分析出来的内存引用图形:

有了这个图就很容易看出来了:myView保持了action,action保持了testMethod,testMethod中由于设置了vc的label因此也保持了VC。因此咱们能够肯定:方法中隐式的self的捕捉策略是strong。这样直接把方法传入子view中会引发引用循环。debug

解决方案

1.将逻辑实如今一个匿名闭包里,不实如今类的方法上

这样就能够本身声明捕捉策略。这样的方式使用就和OC的block相似了:3d

myView = MyView(){ [unowned self] in
            self.label.text = "haha"
        }复制代码

2.在匿名闭包中调用方法

不是直接传入testMethod方法,而是在传入的闭包中调用自身的方法:

myView = MyView(){ [unowned self] in
            self.testMethod()
        }复制代码

欢迎关注个人微博:@没故事的卓同窗

相关连接:
WWDC 2016 Session 410 Visual Debugging with Xcode

相关文章
相关标签/搜索