AFNetworking源码(二)

前言

上海快30岁了月入3W, 为什么还是焦虑?
你焦虑, 是因为你月入三万的姿势不对! 如果你月入三万的组成是:月薪1万, 另外四套房子租金两万, 绝对不会焦虑.

timg.jpg

介绍

  • 这篇文章主要对AFNetworking的对外接口和请求处理文件进行阅读, 会拿出里面的优秀的技术点进行介绍.

一、AFHTTPSessionManager

  • AFHTTPSessionManager类主要是对外提供接口, 通过调用父类方法封装一些常用接口.

AFHTTPSessionManager继承自AFURLSessionManager,并且实现了<NSSecureCoding, NSCopying>两个协议,如果对这两个协议想要深入了解可以点击这里;

A.NSCoder
- (instancetype)initWithCoder:(NSCoder *)decoder;
//解档   扩展 通过XIB创建的一个nib文件,会调用相关类的这个方法
- (void)encodeWithCoder:(NSCoder *)coder;//归档
+ (BOOL)supportsSecureCoding;支持NSSecureCoding
B.clang warning
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
//code
#pragma clang diagnostic pop

表示在这个区间里忽略一些特定的clang的编译警告,因为AFNetworking作为一个库被其他项目引用,所以不能全局忽略clang的一些警告,只能在有需要的时候局部这样做,所以经常见忽略-Wgnu警告的写法,详见这里

C.description
- (NSString *)description {
    return [NSString stringWithFormat:@"<%@: %p, baseURL: %@, session: %@, operationQueue: %@>", NSStringFromClass([self class]), self, [self.baseURL absoluteString], self.session, self.operationQueue];
}

相信跟多人跟我一样经常忽略description,它是NSObject里面带的一个属性,并且携带了一个方法- (NSString *)description. 它就是我们打印对象时所显示的内容,默认状态下, 我们比如打印一个类:

AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
NSLog(@"%@", manager);
// 不重写description
<AFHTTPSessionManager: 0x6000001ccc60>

// 重写description之后输出的使我们自定制内容
<AFHTTPSessionManager: 0x6000001ccc60, baseURL: (null), session: <__NSURLSessionLocal: 0x7faa8f706420>, operationQueue: <NSOperationQueue: 0x60400003cc60>{name = 'NSOperationQueue 0x60400003cc60'}>

所以我大胆猜测其内部实现
- (NSString *)description {
    return  [NSString stringWithFormat:@"<%@:%p>", NSStringFromClass([self class]),&self];
}
注:这是写框架的一些细节, 可以让使用者通过打印的方式更加具体的看到当前对象的一些状态.
D.NSURLSessionConfiguration
  • NSURLSessionConfiguration系统提供了三种配置

[1]Configuration options for an NSURLSession. When a session is
created, a copy of the configuration object is made - you cannot modify the configuration of a session after it has been created.

// 在创建Session对象时,可以通过NSURLSessionConfiguration对象来配置Session.
Session一旦配置完毕,你就不能够修改.

[2]The shared session uses the global singleton credential, cache
and cookie storage objects.

// Default Session 将cache以及credentials存储于本地。

[3]An ephemeral session has no persistent disk storage for cookies,
cache or credentials.

// Ephemeral Session 则对数据更加保密安全一些,
它不会向本地存储任何信息,所有的cache、credentials 等存在内存中并和Session绑定,
当Session销毁时,所有的相关信息也同时自动销毁。

[4]A background session can be used to perform networking operations on behalf of a suspended application, within certain constraints.

// Background Session能够使APP处于后台时,数据继续传输。
其行为与default Session类似,但是所有的数据传输均有一个独立的进程来管理(非本APP)。
同时Background Session也有一些功能上的限制。

下面三种配置的接口提供

@interface NSURLSessionConfiguration : NSObject <NSCopying>

@property (class, readonly, strong) NSURLSessionConfiguration *defaultSessionConfiguration;
@property (class, readonly, strong) NSURLSessionConfiguration *ephemeralSessionConfiguration;

+ (NSURLSessionConfiguration *)backgroundSessionConfigurationWithIdentifier:(NSString *)identifier API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0));

二、AFURLSessionManager

  • AFURLSessionManager这个类才是对请求发送的核心类.它主要包括AFURLSessionManagerTaskDelegate_AFURLSessionTaskSwizzlingAFURLSessionManager这三个对象.
<一>AFURLSessionManagerTaskDelegate
  • 这个类分发处理了AFURLSessionManager主类的上传,下载进度的管理,通过观察task一些方法的变化,来得到进度.而且还帮忙处理了NSURLSessionTaskDelegateNSURLSessionDataTaskDelegateNSURLSessionDownloadTaskDelegate 等代理的返回数据.
A.NSProgress
// 进度暂停回调
@property (nullable, copy) void (^pausingHandler)(void);
// 进度恢复回调
@property (nullable, copy) void (^resumingHandler)(void) NS_AVAILABLE(10_11, 9_0);
注:AFNetworking 对进度的管理也用到了NSProgress,并实现了进度的暂停及恢复.

这里对NSProgress已经做了详细的介绍我不再多做解释.

<二>_AFURLSessionTaskSwizzling

这个类在#issues 1477上reopen了多次,讨论还是很激烈的。讨论的起由是app会莫名crash,主要原因是AFNetworking对NSURLSessionTask中的state进行了KVO操作。一开始人们removeObserver这个state,但是会造成AFNetworkActivityIndicatorManager功能(其中会观察state)削弱。另外后来iOS8上也出现了同样crash现象,貌似iOS7和iOS8在NSURLSessionTask有些不同。最后还是有个大神用swizzling方法才解决了这个问题。

A.任务状态监听
  • AFNetworking巧妙的运用runtime的方法交换在+load中给NSURLSessionTask的两个方法注入监听.
- (void)suspend;
- (void)resume;
image.png
- (void)af_resume {
    NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
    NSURLSessionTaskState state = [self state];
    [self af_resume];
    
    if (state != NSURLSessionTaskStateRunning) { // 这有点不明白这个讨论恒成立state = NSURLSessionTaskStateCanceling;
        // 发送重新开始通知  用于网路指示器的设置
        [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotification object:self];
    }
}
<三>AFURLSessionManager
A.NSURLSession与NSURLConnection
1.NSURLSession在iOS7.0时被Apple提出后, 虽然Apple一直对其良好的API设计大力推广,
然而其能够达到的效果, 似乎一直都和NSURLConnection不相伯仲.

2.由于AFNetworking优秀的架构设计, NSURLSession甚至还不如NSURLConnection好用.
那么,有什么理由切换到NSURLSession? 2015年的WWDC似乎告诉了我们答案.

3.HTTP /2, 2015年5月RFC 7540正式发表的下一代HTTP协议,
是1999年来HTTP 1.1发布后的首个更新. 相对于前一个版本, HTTP /2以快著称.

HTTP.jpg

根据2015的WWDC Session71,我们知道iOS9+,NSURLSession开始正式支持HTTP /2,也就意味着你的网络连接速度也可以有如上图那样的提升。

B.共享NSURLSession思路分析
  • 现在AFNetworking 内部提供的创建方式[AFHTTPSessionManager manager]是单例的不会每次创建一个NSURLSession,那么每次新建一个NSURLSession,然后新建Task,激活Task,完成网络请求有什么问题吗?
在回答这个问题以前,我们先来聊聊网络的通讯协议。
我们也都知道,HTTP协议是基于TCP协议的。所以在每次的HTTP请求之前,客户端和服务器端,
都先需要经过TCP连接的三次握手,即每次请求之前,
网络的数据都已经在客户端和服务器端之间来回了三次。如下图:
三次握手.jpeg
事实上在HTTP 0.9, HTTP 1.0协议的时代,
每次HTTP的请求,都需要先经过TCP的连接,然后才开始HTTP的请求.

那么,为了让我们的请求更快,避免每次都产生一个TCP三次握手,成了一个优化的选项。
于是在HTTP 1.1中,出现了Connection: keep-alive这个选项。这个优化选项,
可以使得客户端和服务器端复用一个TCP连接,从而减小每次的网络请求时间。
  • 没错,共享的NSURLSession将会复用TCP的连接,而每次都新建NSURLSession的操作将导致每次的网络请求都开启一个TCP的三次握手。所以我们封装请求类的时候,最好用单例模式.让manager持有NSURLSession,达到共享的效果.即一个Session创建多个Task来实现网路的请求.
B.delegate的储存
  • self.mutableTaskDelegatesKeyedByTaskIdentifier 是一个储存AFURLSessionManagerTaskDelegate的可变字典.用NSLock锁保证了线程安全.
    image.png
C. responseSerializer
/** 
 当数据传输任务用“GET”、“POST”等方式时,在“dataTaskWithRequest:success:failure:”方法里创建的一个从服务器发回的响应。 
 默认情况下,此属性设置为“AFJSONResponseSerializer”的一个实例。 
 @warning “responseSerializer”必须不为空. 
 */ 
@property (nonatomic, strong) id <AFURLResponseSerialization> responseSerializer;  

AFHTTPRequestSerializerAFHTTPResponseSerializer 都遵循了<AFURLResponseSerialization>协议.
为什么这里提到这个呢, 这种通过协议来整合不同工具的方法,很值得借鉴. 在以后的文章中会详细介绍这两个类.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容