iOS 使用Leak进行内存泄漏的分析
第一次使用Leak对项目进行内存泄漏的检测,也是查阅了许多资料,算是可以做到发现问题并解决问题。
我的项目泄漏情况是这样的:
![](https://img.haomeiwen.com/i1759660/08b8077a50d98c19.png)
满屏的红叉让人心慌,那么如何找到泄露的位置呢?
首先要选中满是红叉的那一行,然后在这里
![](http://upload-images.jianshu.io/upload_images/1759660-89031fd91dfc8c89.png)
选中Call Tree,
在右边
![](http://upload-images.jianshu.io/upload_images/1759660-9a6fc6a1f8c449a0.png)
选中Invert Call Tree 和 Hide System Libraries 两项,Invert Call Tree 的意思是翻转调用树,意思就是我们在调用函数时,是一层一层的,调用外层函数会一直进入内层,直到最后一层,有点递归的意思,当选中 Invert Call Tree 选项时,会直接显示内层函数,方便我们去寻找,否则会直接显示最外层的函数,我们需要将其一层一层展开,比较费劲,不直观。
而Hide System Libraries 的意思很明显了。就是隐藏系统类库,避免一些莫名其妙的,我们无法改动的信息迷惑我们。
那么接下来我们将看到泄漏列表:
![](http://upload-images.jianshu.io/upload_images/1759660-8ed357b912e56a2f.png)
看到这里我震惊了,强大的AFNetworking也会存在泄露?
让我们再看看具体是哪里除了问题,双击那一行我们就可以进入到具体泄漏的那个函数
![](http://upload-images.jianshu.io/upload_images/1759660-afab965fc8d87478.png)
可以看到每一行泄漏的byte大小都标了出来,其中蓝色的为最大。
这里就要思考一个问题了,AFNetworking内部的这些代码我们怎么改呢?
不必担心,我们发现问题出在这个方法
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
一直以来,我都以为这是一个manager是单例,带你进去一看其实不然,
![](http://upload-images.jianshu.io/upload_images/1759660-8baf043e00738ad0.png)
每次调用时都会创建一个新的对象,那么问题又来了,创建就创建呗,难道执行之后,还会不释放?
查阅资料后果真如此,我们使用的 AFHTTPSessionManager 继承自 AFURLSessionManager ,
创建对象时会调用傅父类的方法。
![](http://upload-images.jianshu.io/upload_images/1759660-7e9ef93b513deb2f.png)
点进去看,发现其强引用一个NSURLSession对象,
![](http://upload-images.jianshu.io/upload_images/1759660-8f18f759bb36c9d3.png)
并且将自己设置为了NSURLSession对象的代理
![](http://upload-images.jianshu.io/upload_images/1759660-cbe81abe0be3e9f2.png)
而NSURLSession又是强引用代理
![](http://upload-images.jianshu.io/upload_images/1759660-be41e16e68180173.png)
这样便造成了循环引用,彼此谁也释放不了。
那么如何解决呢?
这里提供两种策略:
(一)NSURLSession提供两个方法:
![](http://upload-images.jianshu.io/upload_images/1759660-f621f2062dac46cc.png)
这个方法会立刻取消当前任务,session对象被释放。那么循环引用不复存在。
![](http://upload-images.jianshu.io/upload_images/1759660-b5e1e8eb13e9de90.png)
而finishTasksAndInvalidate方法,则会等待任务完成时将session释放,消除了循环引用。
我们可以在success 和 failure block中调用这两个方法,个人推荐使用finishTasksAndInvalidate。
(二)像AFNetworking 3.0 提供的DEMO中,是这么用的:
创建一个继承自NSHTTPSessionManager的类,实现单例方法。
![](http://upload-images.jianshu.io/upload_images/1759660-c5ff7a61cc9f3210.png)
然后在自己封装的网络层中修改。