前言
相信不少喜欢对内存进行优化的开发者都注意到了,使用AFNetworking会造成内存泄漏,当然造成内存泄漏的大部分都是这样使用AFNetworking的:
// 创建manager对象
AFHTTPSessionManager * manager = [AFHTTPSessionManager manager];
// 发起请求
[manager POST:@"http://www.host.com" parameters:@{} progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
}];
上面的使用方法很简单,而且也正是AFNetworking推荐我们这样使用的。从上面的代码使用看,我们自己造成内存泄漏问题的概率几乎为0,既然不是我们的问题,那么就是AFNetworking自己的问题。但是你会郁闷,为什么AFNetworking这么出名的一个第三方网络框架,会有这个问题呢?
问题所在
通过翻阅AFNetworking的源码,我找出了具体的原因,如下图:
我们使用的manager对自己内部创建的session有一个强引用,然而manager自身作为NSURLSession的代理,也传给了session,然后这个session为了确保网络数据的正常使用,会对manager也进行一个强引用。因此,这里其实有一个循环引用。
苹果的api文档也告诉我们了,如下图:
解决问题
既然问题找到了,那么就来解决它。
方案1:
现在为止,网上搜到的解决方案:既然是我们创建的manager内存泄漏了,那么很好办,我们就干脆把这个manager弄成一个单例,每次发起请求的时候,就用这个单例manager进行网络请求。因为AFNetworking内部支持发起多个任务,所以使用起来也完全没有问题。
但是我个人认为,这样做其实不太好。参考图一,我们知道网络请求回来的代理回调都是在一个串行队列里执行的。既然是串行队列,那执行任务肯定都是一个一个按照先后顺序来的。如果我们同时发起了4个下载任务(发起的请求是异步的),数据代理回调却是同步的。在效率上肯定是有点影响的
方案2:
这是我自己写的解决方案(其实图2,苹果的文档已经告诉我们了)
AFHTTPSessionManager * hhmanager = [AFHTTPSessionManager manager];
NSURLSession * managerSession = hhmanager.session;
[hhmanager POST:@"http://www.host.com" parameters:@{} progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
// 做你自己的处理
// 在最后的时候执行下面代码
[managerSession finishTasksAndInvalidate];
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
// 做你自己的处理
// 在最后的时候执行下面代码
[managerSession finishTasksAndInvalidate];
}];
在网络请求完成的时候,使对应的session无效,这样就打破了之前我们提到的循环引用,使得manager被释放。