从一个AFNetworking循环引用说起

简介: 本文分析使用代码是AFNetworking 3.2.1最近使用Instruments中的Leaks分析项目内存泄露,发现了一个AFNetworking的循环引用。

本文分析使用代码是AFNetworking 3.2.1

最近使用Instruments中的Leaks分析项目内存泄露,发现了一个AFNetworking的循环引用。如下图所示:

img_d87589dbb97ad7ef55d400d164597e7f.jpe
15354171666142.jpg

通过调用栈发现产生泄露的代码在这里:

// AFURLSessionManager.m
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
    // ... 初始化代码,省略

    // 导致循环引用的方法
    self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];

    // ... 其它初始化代码,省略
    return self;
}

大致原因就是AFURLSessionManager引用NSURLSession,同时设置NSURLSession的delegate为自己,NSURLSession会强引用delegate,于是产生了循环引用。

关于NSURLSession的delegate官方说明:
This delegate object is responsible for handling authentication challenges, for making caching decisions, and for handling other session-related events. The session object keeps a strong reference to this delegate until your app exits or explicitly invalidates the session. If you do not invalidate the session, your app leaks memory until it exits.

解决方案

在AFNetworking官方issues找到了相关的问题Possible memory leak in AFURLSessionManager。作者的回答如下:

img_0732f7735e1a9c2531bc6f4800488303.jpe
15353328585634.jpg

解决方案有两种:

  1. 这是最常见也是作者推荐的方法,只创建一个AFURLSessionManager,整个APP共享,虽然还是有循环引用,但是就没有内存泄露的问题了。
  2. 如果要使用多个AFURLSessionManager,在使用完成后手动调用invalidateSessionCancelingTasks:来断开循环引用。(这种方案不推荐,具体原因看下一小节)

AFURLSessionManager复用

关于AFURLSessionManager是否使用单例这个问题,官方demo使用的是单例,在苹果官方文档找到这么一段话

With the NSURLSession API, your app creates one or more sessions, each of which coordinates a group of related data transfer tasks. For example, if you’re creating a web browser, your app might create one session per tab or window, or one session for interactive use and another for background downloads. Within each session, your app adds a series of tasks, each of which represents a request for a specific URL (following HTTP redirects, if necessary).

我的理解是这样的,根据使用场景的不同,这个问题有不同的答案,在大多数场景下APP都是在和同一服务器打交道,一个session就够了,如果有连接多个服务器、或者后台下载等功能需求,可以给每个服务器、后台下载任务创建单独的session(但是也不能每个请求都单独创建session)。

在查找资料的时候,我发现有博客提到单例seesion可以减少TCP三次握手,动手验证下:

多个网络请求复用一个AFURLSessionManager,连续发两个网络请求,用Wireshark抓包可以看到,第二次网络请求复用了第一次的TCP连接,没有做三次握手。

img_2a35c0f5d09e53b81f436f7b80714c7d.jpe
15354191775340.jpg

下图是每次网络请求都新建一个AFURLSessionManager的抓包,可以看到每个网络请求都进行了TCP三次握手。

img_3b3b81c94702561a587e1ef98d61ce45.jpe
15354193039698.jpg

实验结果的确如网上所说,复用AFURLSessionManager可以减少三次握手,提升效率。

博客链接

目录
相关文章
|
29天前
|
数据采集 Python
再谈re的应用
再谈re的应用
29 2
|
5月前
|
设计模式 Swift iOS开发
【Swift开发专栏】Swift中的协议与委托模式
【4月更文挑战第30天】Swift编程语言强调协议与委托模式。协议定义了类型需实现的方法和属性,如`SomeProtocol`示例。遵循协议的类、结构体或枚举需实现协议要求。协议可继承,也可作为类型使用。委托模式让对象间通信更灵活,通过协议实现,如`DataSourceDelegate`示例。实战案例展示了在`UITableView`和自定义下载器中使用委托模式。
106 0
|
4月前
|
API C++
c++进阶篇——初窥多线程(三)cpp中的线程类
C++11引入了`std::thread`,提供对并发编程的支持,简化多线程创建并增强可移植性。`std::thread`的构造函数包括默认构造、移动构造及模板构造(支持函数、lambda和对象)。`thread::get_id()`获取线程ID,`join()`确保线程执行完成,`detach()`使线程独立,`joinable()`检查线程状态,`operator=`仅支持移动赋值。`thread::hardware_concurrency()`返回CPU核心数,可用于高效线程分配。
|
前端开发
前端学习笔记202306学习笔记第四十二天-深拷贝解决循环引用问题1
前端学习笔记202306学习笔记第四十二天-深拷贝解决循环引用问题1
55 0
|
前端开发
前端学习笔记202306学习笔记第四十二天-深拷贝解决循环引用问题2
前端学习笔记202306学习笔记第四十二天-深拷贝解决循环引用问题2
64 0
|
存储 安全 Java
C++第十一节——单例模式 C++11 智能指针 异常 有关讲述
可以用同样的方式来实现,就是将构造函数私有化,然后让创建类的时候只能通过一个接口函数来实现,而在这个接口函数中我们将其创建在栈上。
400 3
C++第十一节——单例模式 C++11 智能指针 异常 有关讲述
|
iOS开发
iOS开发 - 打包静态framework后,引用时必须做的一件事,否则崩溃
iOS开发 - 打包静态framework后,引用时必须做的一件事,否则崩溃
188 0
|
iOS开发
afnetworking 内存泄漏问题
afnetworking 内存泄漏问题
360 0
afnetworking 内存泄漏问题
|
C语言 iOS开发
OC 底层知识(二): KVO
OC 底层知识(二): KVO
184 0
OC 底层知识(二): KVO
|
API
OC底层知识(三): KVC
OC底层知识(三): KVC
180 0
OC底层知识(三): KVC