如何在iOS中自动检测内存泄漏

iOS开发人员面临的两个最大问题是泄漏或保留周期。 两者都可能给应用带来一些弊端,例如高内存消耗,随机崩溃,性能下降等。

因此,在Wolox,我们决定研发技术来避免这些问题,开发出功能强大的软件,并最终使开发人员的工作变得更加轻松。

什么是泄漏?

当给定的存储空间无法被系统恢复时,由于无法告知该存储空间是否在实际使用中,会发生内存泄漏。

在iOS中产生内存泄漏的最常见问题之一是保留周期。 当我们在两个或多个对象之间进行循环引用时,就会发生这种情况。 保留周期可防止释放这些对象使用的内存,即使这些对象的创建者释放了它们。

例如,如果我们有一个班级人员和一个班级公寓(如下所示)

Swift和Objective-C都有一个引用计数器(ARC),负责释放未引用的内存(换句话说,就是不使用的内存)。 此过程通过计算每个对象具有的强引用来工作。

强引用将引用计数器增加一,而弱引用则根本不增加计数器(当对象的引用计数达到零时,它们会将值设置为nil)。 当实例具有零引用时,它将被释放。 但是,ARC无法检测到保留周期。

在我们的情况下,我们只有强大且交叉的引用,因此,这两个实例将永远不会被释放。

如何修复泄漏?

我们所能做的就是声明一个引用为弱引用,另一个声明为强引用,因此,循环引用被破坏了。

发现此类问题的两种常见情况包括:

  • 案例1:关闭
  • 情况2:委托模式

当一个对象的强引用(我们称其为ObjectA)强烈引用另一个对象(我们称其为ObjectB),而ObjectB强烈引用该闭包时,则可能发生由闭包创建的保留周期。 在下图中,我们可以看到所描述场景的表示。

情况1:可以使用`unown`或`weak`关键字来解决由闭包引起的保留周期,从而破坏循环引用。

另一方面,当未将委托声明为对委托具有强引用的弱对象时,可能会发生使用委托模式(情况2)引起的保留周期。 例如,如果我们有一个视图控制器实现了另一个对象的委托,而这个委托没有被声明为弱对象,我们将得到如下图形:

有关内存管理的更多信息,请查看以下链接:http://krakendev.io/blog/weak-and-unowned-references-in-swift

结合分析仪

在Wolox,我们使用MVVM模式使苗条的视图控制器更加有序,更小并且分开职责。

结合分析仪是我们用来检测泄漏的最早工具之一。 此过程包括评估在视图控制器中实现的绑定数量和未绑定数量之间的差异。 通过绑定,我们的意思是将视图模型的属性连接到控制器的视图出口和动作。 使用这个数字,我们可以区分发布的视图控制器和视图模型与未发布的视图控制器和视图模型。

我们必须小心,不要检测到由寿命长的对象(在应用程序的整个生命周期中保持活动的对象)引起的虚假内存泄漏,例如管理选项卡栏的视图模型。 为了做到这一点,我们有一个“注册表”来跟踪这个长期存在的对象。 该注册表是使用包含该长期对象的标识符的单例数组实现的。

另外,每次执行视图模型的绑定或取消绑定时,我们都需要注册和删除。 为此,我们还有一个带有结构数组的单例,其中包含我们注册的视图模型的数据。 此外,该类将分析是否删除了注册为“长期存在”的视图模型,该模型不应发布。

有关更多信息,请查看:带情节提要的ViewControllers中的ViewModel注入

完成registerUnbinding和registerBinding函数之后,我们可以注册绑定或取消绑定视图模型。 该方法如下:

最后,我们需要添加一个按钮来触发该工具。

实施新功能后,我们可以使用它来运行BindingAnalyzer来检查应用程序内是否存在任何泄漏。

绑定分析器工具是我们在Wolox使用的一种工具,可以产生出色的结果。 即使使用此工具,仍然有可能发生泄漏,因为每次代码库发生更改时,我们都必须手动运行该工具。

使用单元测试自动检测内存泄漏

绑定分析器工具引起的问题使我们思考如何使用单元测试自动检测泄漏。

通常,当我们添加新功能时,我们会测试其行为,但是是否会在添加新漏洞时进行测试?

在Wolox,我们将泄漏测试用于诸如视图控制器和视图模型之类的功能。 使用该技术可以省去手动检查泄漏的过程。 一种实现方法是使用Nimble和Quick。

如您所见,测试由两部分组成,第一部分负责检查视图控制器和视图模型中是否都存在泄漏。

  • 给定一个视图控制器工厂,将创建一个引用以前模型的新控制器。
  • 因此,它必须对视图控制器以及视图模型具有弱引用。
  • 现在,删除与视图控制器关联的强引用并隐藏其视图。 这应该允许视图控制器和视图模型被释放。
  • 最后,检查视图控制器和视图模型是否已正确释放。 如果断言失败,则在视图控制器或视图模型中发现一个或多个泄漏。

建议使用稍有延迟的其他队列,以确保视图控制器和视图模型有适当的时间释放。

如何实施工厂:

测试的第二部分在于检测是否所有类都从UIViewController扩展并将它们与存在的工厂数量进行比较。 如果我们错过一个或多个测试,测试将失败(不断提醒您测试新功能)。 要检测从另一个扩展的所有类,我们可以执行以下操作:

我们越早发现漏洞,就越有可能避免并在代码库中修复漏洞。 当项目正在发展时,这一点尤其重要,因为它将防止以后出现许多困难。

尽管此工具仍处于开发阶段,但已取得了出色的成果。 我们希望与其他项目和团队一起继续改进和测试此框架。 最终目标是使其成为开源,敬请期待!

随时在下面发表评论,问题或建议。

Interesting Posts