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

    https://developer.apple.com/videos/play/wwdc2016/410/

Xcode8的调试技能Memory Graph 实战解决闭包引用循环问题的更多相关文章

  1. 11个强大的 Visual Studio 调试技能

    简介 调试是软件开辟周期中很首要的一项目组.它具有挑衅性,同时也很让人困惑和懊恼.总的来说,对于稍大一点的法度,调试是不成避免的.比来几年,调试对象的成长让很多调试任务变的越来越简单和省时. 这篇文章 ...

  2. Xcode6 运行程序后,右侧Debug区域的Memory显示空白解决方法

    http://chenyh-blog.com/%E8%9B%8B%E7%96%BC%E7%9A%84%E5%86%85%E5%AD%98-%E7%AC%AC%E4%B8%89%E7%AF%87-sdw ...

  3. Vivado简单调试技能

    Vivado简单调试技能 1.关于VIO核的使用 首先配置VIO核: 配置输入输出口的数量5,5 配置输入口的位宽 配置输出口位宽和初始值. 例化与使用: vio_0 U1 ( .clk(clk_27 ...

  4. PHP内存溢出Allowed memory size of 解决办法

    PHP内存溢出Allowed memory size of 解决办法 博客分类: php   ============================Allowed memory size of  x ...

  5. ORA-27125: unable to create shared memory segment的解决方法(转)

    ORA-27125: unable to create shared memory segment的解决方法(转) # Kernel sysctl configuration file for Red ...

  6. VSCODE 针对调试C语言时一闪而过解决办法

    针对调试C语言时一闪而过解决办法 前提: 已经按照 C/C++ 已经安装 MINGW(并配置完成) 原因:  主要是因为tasks的配置没有写对 解决办法: tasks.json { // See h ...

  7. spring如何解决单例循环依赖问题?

    更多文章点击--spring源码分析系列 1.spring循环依赖场景2.循环依赖解决方式: 三级缓存 1.spring循环引用场景 循环依赖的产生可能有很多种情况,例如: A的构造方法中依赖了B的实 ...

  8. android 真机调试出现错误 INSTALL_FAILED_INSUFFICIENT_STORAGE 的解决方法。

    关于这个神奇的 内存不够错误的通常解决方法,网上大把,建议大家在尝试过了网上的方法后再来尝试下我的这种方法. 编译工具: android studio 测试真机:米 2 调试的时候出现:INSTALL ...

  9. 应该具备的调试技能(java)

    ------Java部分---------- 1. tomcat在eclispe中怎样启动调试模式2. 带有main方法的Java应用程序怎样启动调试模式3. 调试在eclispe中的快捷键 F5 F ...

随机推荐

  1. 【BZOJ】【1091】【SCOI2003】切割多边形

    计算几何+枚举 我比较傻逼……一开始想了个贪心,就是这样:

  2. 怎样在centos安装python-mysql?

    在python中使用mysql,须要安装mysql-python依赖包, 能够通过pip来安装: pip install MySQL-python 假设错误发生,须要先安装一个开发包: yum ins ...

  3. 第十一章 企业项目开发--消息队列activemq

    注意:本章代码基于 第十章 企业项目开发--分布式缓存Redis(2) 代码的github地址:https://github.com/zhaojigang/ssmm0 消息队列是分布式系统中实现RPC ...

  4. Anagrams leetcode java

    题目: Given an array of strings, return all groups of strings that are anagrams. Note: All inputs will ...

  5. c#逐行分元素读取记事本txt数据写进数据库

    其实这里最关键的一个方法是 StreamReader类里的 ReadLine();这个方法可以逐行读取txt流里面的数据.写了个简单的demo,已经加上了详细的注释说明.   ok,好了,不废话,下面 ...

  6. 输错密码?这个 sudo 会“嘲讽”你

    导读 你在 Linux 终端中会有很多的乐趣.我今天要讲的不是在终端中跑火车.我今天要讲的技巧可以放松你的心情.你学习过如何在命令行中增加 sudo 命令的超时,今天的文章中,我会向你展示如何让 su ...

  7. 2013级C++第14周(春)项目——多态性、虚函数和抽象类

    课程首页在:http://blog.csdn.net/sxhelijian/article/details/11890759,内有完整教学方案及资源链接 第一部分 阅读程序1.阅读.改动和执行关于交通 ...

  8. 简单账本-用完即走的微信小程序

    作为一个记账强迫症患者,对当前手机中的记账App都不太满意.这类软件越来越臃肿,越来越慢,启动要半天.联网同步要半天,进入界面又有一堆新功能要介绍.好不容易开始记账,又得各种高大上的选择设定,一笔帐下 ...

  9. UML建模学习1:UML统一建模语言简单介绍

    一什么是UML? Unified Modeling Language(UML又称为统一建模语言或标准建模语言)是国际对象管理组织OMG制定的一个通 用的.可视化建模语言标准.能够用来描写叙述(spec ...

  10. 从Ubuntu12.04LTS到Foreda19再到Foreda8

    装Ubuntu的初衷是以为它能识别我的PCI无线网卡,但装了两遍没有做到. 昨天在Ubuntu装jdk7,其过程与正常Linux安装jdk差别不小,有点背离的意思.另外VI的用法也和正常Unix/Li ...