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/

时间: 2024-08-07 08:22:23

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

【C/C++学院】0816-引用包装器/仿函数/转义字符 R”()”/using别名/模板元编程 比递归优化/智能指针/多线程/静态断言以及调试技能的要求 assert

引用包装器  std::ref(变量) #include<iostream> template<class T> void com(T arg)//模板函数,引用无效,引用包装器 { std::cout <<"com ="<< &arg << "\n"; arg++; } void main() { int count = 10; int & rcount = count; com(coun

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

PHP内存溢出Allowed memory size of 解决办法 博客分类: php ============================Allowed memory size of  xxx bytes 以前追踪过这个问题,但是那个时候工具用的不太好,没看的这么细,这次搞的比较细,修正了偶以前的看法 .于是写小文一篇总结一下. PHP偶尔会爆一下如下 错误Allowed memory size of  xxx bytes exhausted at xxx:xxx (tried to a

使用Genymotion调试出现错误INSTALL_FAILED_CPU_ABI_INCOMPATIBLE完美解决办法

使用Genymotion调试出现错误INSTALL_FAILED_CPU_ABI_INCOMPATIBLE完美解决办法 点击下载 | 密码: 35r5 直接将下载的东西拖到模拟器中,点击OK,然后从其模拟器就ok了

在虚拟机调试反VM程序的解决方法

在虚拟机调试反VM程序的解决方法 只针对VMware ,只要在.vmx文件中加入以下命令就可以了 isolation.tools.getPtrLocation.disable = "TRUE"isolation.tools.setPtrLocation.disable = "TRUE"isolation.tools.setVersion.disable = "TRUE"isolation.tools.getVersion.disable = &q

ARC之解决闭包引起的循环强引用

copy自:swift2.0官方教程中文版---极客学院版 1.产生闭包之间的循环强引用 直接代码和上图: class HTMLElement { let name: String let text: String? lazy var asHTML: Void -> String = { if let text = self.text { return "<\(self.name)>\(text)</\(self.name)>" } else { retu

[原]排错实战——解决Tekla通过.tsep安装插件失败的问题

原总结调试排错troubleshootteklaprocess monitorsysinternals 缘起 最近同事使用.tsep安装Tekla插件的时候,Tekla提示该插件已经存在了,需要卸载后再安装.同事找了半天,没找到怎么卸载,我也没找到.于是决定弄清楚Tekla是 如何判断一个插件安装与否的. 想必看过我其它排错文章的小伙伴已经猜到了,我们今天文章中的主角依然是process monitor.好了,话不多说,好戏马上开始. 调查 在开始调查之前,我们可以先思考下,Tekla是如何判断

eclipse调试找不到源解决办法

eclipse调试时有时显示找不到源码,首先得确定代码没问题 这是eclipse没有发现工程源码,解决办法是 右键工程>>Debug As >> Debug configurations 选择添加使用的工程,顺便可以将Default 删除掉 到此就大功告成,重新将项目启动即可

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

关于这个神奇的 内存不够错误的通常解决方法,网上大把,建议大家在尝试过了网上的方法后再来尝试下我的这种方法. 编译工具: android studio 测试真机:米 2 调试的时候出现:INSTALL_FAILED_INSUFFICIENT_STORAGE 按照字面意思理解为:因为内存满了而导致安装失败. 要先确定手机内存还有足够的空间时,就可以排除非内存不够引起的. 我的解决方法,手机连接电脑,手动通过电脑安装一个任意的 apk,然后再调试,就能解决了,神奇吧,原因不知为何.

Activex调试以及m_hWnd为空 解决办法

1. 点击[开始]->[运行] 命令:regedit.2. 定位到HKEY_LOCALMACHINE -> SOFTWARE -> Microsoft -> Internet Explorer -> Main3. 在[右边区域][右键]新建一个名称为TabProcGrowth的DWORD值, 数值数据设置为0. VS不用重启,直接可以按F5进行调试ActiveX了! 参看: http://social.microsoft.com/Forums/en-US/vsdebug/th