常见泄漏的点
- Retain Cycle,Block强引用
- NSTimer释放不当
- 第三方提供方法造成的内存泄漏
- CoreFoundation方式申请的内存,忘记释放
1. Block引用内存泄漏问题:
[cell setSelectTagCityBlock:^(NSIndexPath *indexPath, NSInteger index){
[self tableView:_tableViewCityList didSelectRowAtIndexPath:indexPath index:index];
}];
利用__weak防止Block循环引用方法:
//创建__weak弱引用,防止强引用互相持有 __weak __typeof(self)weakSelf = self; AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) { //创建局部__strong强引用,防止多线程情况下weakSelf被析构 __strong __typeof(weakSelf)strongSelf = weakSelf; strongSelf.networkReachabilityStatus = status; if (strongSelf.networkReachabilityStatusBlock) { strongSelf.networkReachabilityStatusBlock(status); } }; weak 本身是可以避免循环引用的问题的,但是其会导致外部对象释放了之后,block 内部也访问不到这个对象的问题,我们可以通过在 block 内部声明一个 strong 的变量来指向 weakObj,使外部对象既能在 block 内部保持住,又能避免循环引用的问题 block 本身无法避免循环引用的问题,但是我们可以通过在 block 内部手动把 blockObj 赋值为 nil 的方式来避免循环引用的问题。另外一点就是 block 修饰的变量在 block 内外都是唯一的,要注意这个特性可能带来的隐患。
2. Timer不被释放引起的内存泄漏:
_timer = [NSTimer timerWithTimeInterval:[refreshTime integerValue]
target:self
selector:@selector(doFSearchDoubleBackNumberRequest:)
userInfo:searchResult
repeats:NO];
[[NSRunLoop mainRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
Timer 添加到 Runloop 的时候,会被 Runloop 强引用。
Timer 又会有一个对 Target 的强引用。
所以说如果不对Timer进行释放,Timer的targer(self)也一直不会被释放。
有时候我们我们对某个Timer的targer设置了nil。但没设置[timer invalidate]。
其实这个对象还是没被释放的。timer对应的执行方法也一直会在线程中执行。容易造成内存泄露。
注:repeats:NO不会强引用
常规的监测方法
-
Analyze静态分析 (command + shift + b)
主要分析以下四种问题:
1、逻辑错误:访问空指针或未初始化的变量等;
2、内存管理错误:如内存泄漏等;
3、声明错误:从未使用过的变量;
4、Api调用错误:未包含使用的库和框架。
静态分析结果会有警告提示
- Instruments中的Leak动态分析内存泄漏
product->profile ->leaks 打开工具主窗口
点击暂停,将鼠标移到叉号上面点击锁定,点击下方的“田”字格,选择callTree,
选择中间的齿轮,选中选项中的 invert Call Tree 和Hide System Libraries。
Call Tree选项说明: Separate by Thread:按线程分开做分析,这样更容易揪出那些吃资源的问题线程。特别是对于主线程,它要处理和渲染所有的接口数据,一旦受到阻塞,程序必然卡顿或停止响应。 Invert Call Tree:反向输出调用树。把调用层级最深的方法显示在最上面,更容易找到最耗时的操作。 Hide System Libraries:隐藏系统库文件。过滤掉各种系统调用,只显示自己的代码调用。 Flattern Recursion:拼合递归。将同一递归函数产生的多条堆栈(因为递归函数会调用自己)合并为一条。
双击左边 Call Tree 窗口里的任意一行,查看内存泄漏的代码位置:
- Allocation工具了解内存的分配情况
每次点击generations(是两个时间标记之间所有仍然活着的对象的快照)生成快照,而且 Allocations 会记录从上回内存快照到这次内存快照这个时间段内,新分配的内存信息,数次 push 跟 pop 之后,内存还不断增长,则有内存泄露。
参考Link:https://www.jianshu.com/p/9fc2132d09c7