Xcode8的调试技能Memory Graph 实战解决闭包引用循环问题
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 实战解决闭包引用循环问题的更多相关文章
- 11个强大的 Visual Studio 调试技能
简介 调试是软件开辟周期中很首要的一项目组.它具有挑衅性,同时也很让人困惑和懊恼.总的来说,对于稍大一点的法度,调试是不成避免的.比来几年,调试对象的成长让很多调试任务变的越来越简单和省时. 这篇文章 ...
- 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 ...
- Vivado简单调试技能
Vivado简单调试技能 1.关于VIO核的使用 首先配置VIO核: 配置输入输出口的数量5,5 配置输入口的位宽 配置输出口位宽和初始值. 例化与使用: vio_0 U1 ( .clk(clk_27 ...
- PHP内存溢出Allowed memory size of 解决办法
PHP内存溢出Allowed memory size of 解决办法 博客分类: php ============================Allowed memory size of x ...
- ORA-27125: unable to create shared memory segment的解决方法(转)
ORA-27125: unable to create shared memory segment的解决方法(转) # Kernel sysctl configuration file for Red ...
- VSCODE 针对调试C语言时一闪而过解决办法
针对调试C语言时一闪而过解决办法 前提: 已经按照 C/C++ 已经安装 MINGW(并配置完成) 原因: 主要是因为tasks的配置没有写对 解决办法: tasks.json { // See h ...
- spring如何解决单例循环依赖问题?
更多文章点击--spring源码分析系列 1.spring循环依赖场景2.循环依赖解决方式: 三级缓存 1.spring循环引用场景 循环依赖的产生可能有很多种情况,例如: A的构造方法中依赖了B的实 ...
- android 真机调试出现错误 INSTALL_FAILED_INSUFFICIENT_STORAGE 的解决方法。
关于这个神奇的 内存不够错误的通常解决方法,网上大把,建议大家在尝试过了网上的方法后再来尝试下我的这种方法. 编译工具: android studio 测试真机:米 2 调试的时候出现:INSTALL ...
- 应该具备的调试技能(java)
------Java部分---------- 1. tomcat在eclispe中怎样启动调试模式2. 带有main方法的Java应用程序怎样启动调试模式3. 调试在eclispe中的快捷键 F5 F ...
随机推荐
- Ubuntu 常用命令大全
Ubuntu 常用命令大全查看软件 xxx 安装内容#dpkg -L xxx查找软件#apt-cache search 正则表达式查找文件属于哪个包#dpkg -S filename apt-file ...
- 混沌数学之Arnold模型
相关软件混沌数学之离散点集图形DEMO 相关代码: class ArnoldEquation : public DiscreteEquation { public: ArnoldEquation() ...
- asm rebalance 原理
详见原文博客链接地址: asm rebalance 原理
- Maximum Depth of Binary Tree leetcode java
题目: Given a binary tree, find its maximum depth. The maximum depth is the number of nodes along the ...
- MyBatis两张表字段名相同产生的问题
MyBatis两张表字段名相同, 会导致bean属性都映射为第一个表的列, 解决方法: 通过设置别名的方式让其产生区别,如 <select id="queryBySekillId&qu ...
- nginx 域名绑定 域名, nginx 域名绑定 端口
一.nginx 域名绑定 域名 nginx绑定多个域名可又把多个域名规则写一个配置文件里,也可又分别建立多个域名配置文件,我一般为了管理方便,每个域名建一个文件,有些同类域名也可又写在一个总的配置文件 ...
- IOS网络访问详解
第一.访问网络的方式 同步请求:数据的请求过程是由主线程发起的,网络加载需要一定的时间,因此会堵塞主线程 异步请求:数据的请求在多线程中完成 同步请求无法取消,异步请求的过程中可以取消,同步请求无法监 ...
- 关于COM组件log的位置
进程内组件写的log,如果不指定路径直接提供文件名,log文件的位置在dll所在的目录中. 进程外组件写的log,如果不指定路径直接提供文件名,log文件的位置不在exe所在的目录中,而是在系统目录. ...
- idea中dependencies中总是有红色波浪线(缺少dependency)的解决办法
使用IDEA进行maven开发时,将新项目import进工作空间时,Maven Projects栏中的dependencies中总是有红色波浪线,如下图: 但是这些jar在我本地的maven仓库中实际 ...
- 【js】利用闭包消除回调函数启动时值已经发生变化的影响
在以下代码中,timeFun异步执行了一个匿名函数,当输出color的值时已经由green变成red,因此输出为red. function timedFun(callback){ setTimeout ...