美文网首页Des's iOS iOS开发心得架构师之路
Xcode8调试黑科技:Memory Graph实战解决闭包引用

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

作者: 没故事的卓同学 | 来源:发表于2016-10-16 00:56 被阅读13833次

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



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



    那么通过一个实际项目来练习一下吧。
    首先我们写了一个自定义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。
    注意,划重点了!


    这里体现了swift函数式的特性:函数可以自由的当做一个变量传递。

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

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

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

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


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



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



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

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

    有了这个图就很容易看出来了:myView保持了action,action保持了testMethod,testMethod中因为设置了vc的label所以也保持了VC。所以我们可以确定:方法中隐式的self的捕捉策略是strong。这样直接把方法传入子view中会引起引用循环。

    解决方案

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

    这样就可以自己声明捕捉策略。这样的方式使用就和OC的block类似了:

            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

    相关文章

      网友评论

      • 超_iOS:能在补充点么?没太看懂:joy:
      • 荔枝lizhi_iOS程序猿:还不知道的功能
      • b430595f0525:对于项目简单的用起来还可以,项目复杂这个东西真的不好用
      • ChoiKarl:很多时候内存泄露抓不到啊,不知道是以什么样的机制去抓的
      • 小_夭:楼主,按照你文章写的demo并没有出现后面的循环引用等情况,有demo分享一下不?
      • 言溪Lee:博主这种情况好像没用过 一般是在在cell.swift里声明一个闭包作为参数(weak var tipClickColsure:((id:Int, count:Int, isSelected:Bool)->Void))!进行数值传递,类似代理的作用 初始化cell调用这个参数在闭包中处理代码
        cell.tipClickColsure = {[unowned self]
        (id, count, isSelected) in
        ......
        } 这种情况该如何处理呢
      • 言溪Lee:解决方案里MyView(){}是什么写法?😓第一次见这种写法
        没故事的卓同学:@xiaoheziQH 尾闭包,一种语法糖
      • angelababa:请问 使用 [unowned self] 和[weak self] 有什么区别呢
        我把今生当成了来世:@angelababa weak会回收吧,unowned会成为野指针
      • 逆行风:好文章就要赞,不赞不地道
      • SHY圆圆圈圈圆圆:测试了一下,发现会存在该问题,但是这个界面下没有紫色感叹号是什么情况。
      • 一条渴死的鱼:OC源代码的工程貌似没有能看到闭环啊
        一条渴死的鱼:@一条渴死的鱼 而且确定有循环引用
      • 了了此心:我这边发现有一个字符串存在循环引用,但是不知道是哪个类?不知道如何排查,能否指点一下
      • 612b500d03e5:swift3 一个网络请求通过逃逸闭包返回,如果成功再调用一个网络请求继续通过闭包返回,debug模式没有问题,release下崩溃:smile:
      • 只有NO1:我怎么觉得不对啊,在匿名闭包里调用方法这里,swift官方文档说,在闭包里调用方法不会循环引用,因此不加unown self
        zhouwude:@只有NO1 这里使用的是全局的所以会有这种问题
      • c50765594d45:好厉害!!!
      • GJCode:好厉害学习了
      • 没梦想的咸鱼2:oc 不能用吗 :disappointed_relieved:
      • ChenJZ:学到新东西了
      • GJCode:666
      • butterflyer:为啥test method 里的方法不走啊。。
        Origheart:@butterflyer 你需要调用这个闭包,才会走的~
      • MYS_iOS_8801:太感谢了
      • KCatherine:刚好最近遇到控制器无法释放的问题,作者真是雪中送炭:+1:
      • JanzTam:配图太6!:joy:
      • sim_cai:有东西
      • 王小宾:函数是值类型吗?
        六阿哥:明显不是吧
      • Sheepy:666
      • 一缕殇流化隐半边冰霜:这个好啊!!!
        4023b5cb645c:saber喊你回家吃饭了
        一缕殇流化隐半边冰霜:@冰熊熊熊 ???:stuck_out_tongue_winking_eye:
        a7642f69975b:熟悉的面孔

      本文标题:Xcode8调试黑科技:Memory Graph实战解决闭包引用

      本文链接:https://www.haomeiwen.com/subject/nksoyttx.html