使用Leaks定位iOS内存泄漏问题并解决
前言
内存泄漏问题一直是程序开发中最令人头疼的问题,特别是C/C++。虽然C/C++在C++11之后引入了许多新特性,包括智能指针,自动类型推导等,但C++中动态内存的分配和释放仍然需要程序员来显式地进行。
在Objective-C中,内存管理主要依赖于引用计数机制,每当一个对象被创建或被复制时,它的引用计数初始化为1。当创建一个对象的副本或增加一个引用时,对象的引用计数会增加;当释放一个对象或减少一个引用时,对象的引用计数会减少;当对象的引用计数降到0时,系统会自动释放该对象占用的内存。可以看到OC的内存管理机制需要程序员有意识地管理对象的生命周期,这一点和C/C++相似。但OC逐渐开始支持ARC,程序员不再需要显式地管理对象的生命周期,但ARC的实现机制却与C/C++不同,它通过编译器的优化,自动地管理对象的生命周期。即在编译过程中,ARC会自动地插入retain
、release
和autorelease
等方法,以实现对象的生命周期管理。
在OC/C/C++混编中,为了检查、定位及解决内存泄漏的问题,Xcode提供了检查内存泄漏的相关工具,而今天主要介绍的是Instruments工具中的Leaks工具。
Leaks工具主要用来检测malloc
分配出来的内存块,而对于非malloc的内存块则不支持检测。
Leaks内存泄漏检测实践
实践软硬件:
-
Xcode Version 15.4 (15F31d)
-
iOS开发工具链17.5
-
模拟器 iphone 15 pro (iOS 17.5)/ 真机 iPhone 11 pro max(iOS 17.5)
-
开发机器:MacBook Pro (18GB, APPLE M3 pro)
声明: 本文涉及相关内容仅适用于当时开发环境,仅可做参考。在进行操作时,请注意备份,谨慎操作!
-
假设你已经使用Xcode创建了一个工程,并完善了其功能,现在需要来检测内存是否泄漏。打开Xcode中
open Developer Tool
,选择Instruments
,在Instruments
中选择Leaks
。
-
在
Leaks
左上方可以选择对应检测的机器和进程。
-
在XCode中运行对应APP,然后点击
Leaks
工具中左上角的小红点,开始监测。 -
在运行APP时,我们进行各项操作,
Leaks
会动态监测APP是否出现了内存泄漏,当出现下图中的红色叉号时,表明出现了内存泄露。如果没有内存泄漏则会出现对号,这一显示大概每十秒更新一次。
-
点击如下图所示的
Leaks
所在的那一行,将Leaks
工具中的Allocations
选项卡切换到Leaks
选项卡。并把下图中步骤2的Leaks
改为Call Tree
。
- 点击
Leaks
下面的Call Tree
选项,勾选Invert Call Tree
和Hide System Libraries
按照网上博客的说法,上面四个选项分别表示
Separate by Thread 按线程分割
Invert Call Tree 反转调用顺序
Hide System Libraries 隐藏系统库
Flatten Recursion 展平递归
我们勾选了反转调用顺序和隐藏系统库,就可以更加便捷地看到是哪里发生了内存泄漏;
- 现在你已经可以看到哪些地方发生了内存泄漏和内存泄漏的相关信息;
内存泄漏位置定位
上文已经找到了哪些函数发生了泄漏,还定位不到具体泄漏的位置。
如果你已经幸运地配置好了,那么你现在双击泄漏的函数就可以跳转到代码中的具体位置。如果你的不行,那么只需要简单配置一下就可以。
- 在Xcode的工程文件的配置
Build Settings
中,搜索Debug Information Format
,默认情况下,该选项下Debug
是DWARF
,修改为DWARF with dSYM File
。
- 重新编译,运行APP, 启动
Leaks
检测,重复上文查找内存泄漏的步骤,双击泄漏的地方,现在可以定位到代码部分
- 针对内存泄漏问题进行完善,并重复检查以尽量避免内存泄漏。
OC中的ARC和MRC
在OC语言中,ARC是自动引用计数机制,MRC是手动引用计数机制。ARC和MRC的区别在于,ARC会自动管理对象的生命周期,而MRC则需要程序员手动管理对象的生命周期。
如果在工程的Build Settings
中搜索Automatic Reference Counting
,可以找到该选项,该选项是YES
,表示使用ARC。将Automatic Reference Counting
设置为NO
,表示使用MRC。
在本人实践中,通过Xcode创建的工程, Automatic Reference Counting
默认为YES
。
但本人在实践中遇到,部分oc代码使用了ARC,仍然会发生内存泄漏,貌似ARC没起作用,使用手动释放内存后,发现内存泄漏减少了。这不符合预期的情况,使我一度非常疑惑。
经过不断检查,发现在本工程项目中的一个子工程中,Automatic Reference Counting
的选项为NO
。所以之前部分oc代码使用的是MRC,这也是为什么ARC没有起作用的原因。
将上述子工程的ARC启用后,发现内存泄漏的问题被解决。
上述实践也说明了在使用MRC时,如果程序员不能够很好地释放内存,将会发生内存泄漏。即使我使用MRC,并且手动释放,也没有释放干净,还是出现部分内存泄漏,这也是为什么现在都使用ARC的原因。它减少了我们对于内存管理的关注。
TIPS:上述没有默认启用MRC的子工程,是由Cmake生成的Xcode工程,可能其中有些设置和从Xcode创建的工程不同,所以导致了这样的问题。因此如果使用Cmake创建了Xcode工程,一定要确认工程设置正确,不止包括这个ARC的设置。
小结
本文主要由本人在实践中遇到的内存泄漏问题说起,介绍了Xcode中的内存泄漏工具Leaks,并进行了实践和内存泄漏位置定位。最后总结了本人在实践中遇到的一些关于内存泄漏的问题和注意事项。
参考文章
1分钟学会Instrument Leaks检测内存泄漏
iOS 内存泄漏检测 Instruments Leaks
iOS内存深入探索之Leaks
如果本文对你有帮助,请给我一个免费的赞,如果有错误,也欢迎向我反馈!