跳过正文
  1. Posts/

从一个AFNetworking循环引用说起

··3 分钟

2018.8.28

本文分析使用代码是AFNetworking 3.2.1

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

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

// 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。作者的回答如下:

解决方案有两种:

  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连接,没有做三次握手。

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

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

相关文章

iOS模拟器安装debug包

··2 分钟
2018.8.21 由于项目是Hybrid的平台,有第三方人员需要在上面开发H5应用,但是release包不能调试H5,只有debug报可以调试,但是项目源码不能交给第三方,在google搜索了下,发现一篇霜神的文章,讲的是debug包可以拷贝到其它机器运行,于是实践了一波。

dispatch_sync死锁问题

··1 分钟
2018.8.3 问题分析 # 使用dispatch_sync的时候要小心谨慎,稍不注意就会导致死锁问题,先看两个典型的案例:

UIWebView获取详细浏览记录

··4 分钟
2018.8.1 需求 # 获取UIWebView的前进后退的浏览记录,举个例子,比如从A->B->C->B,此时B上一页是A,下一页是C,需要获取A、C的URL信息。

runtime实现私有变量搜索

··2 分钟
本文 Demo 地址 2018.7.31 需求 # 在开发功能时,为了满足产品变态的需求,难免有系统类提供的API不够用的时候,这时候私有变量就可以发挥它光和热了。怎么通过一个类,一层一层的找到特定类型的私有成员变量? 受益于Objective-C的动态语言特性,就算苹果UIKit不开源,但是在runtime面前,类的结构还是暴露无遗。我的思路是逐层手动打印成员变量信息,如果是UI控件可以用Reveal来加快进度,配合KVC机制,获取私有变量就如同探囊取物一般。 在多次遇到这个问题后,我决定实现一个工具类来简化这个过程,毕竟能自动化的就尽量不要手动。

iOS横竖屏总结

··3 分钟
2018.7.23 使用Auto Layout来进行布局就不用自己去监听横竖屏事件了,只需要绘制多套布局即可。但是项目有很多页面是自己手动计算的,于是只有想办法再旋转屏幕时重新布局。