全篇使用的案例Demo
最简单的AFN使用例子,本文使用的是AFNetworking v3.2.1
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager POST:@"https://www.baidu.com" parameters:@{} progress:^(NSProgress * _Nonnull uploadProgress) {
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"成功");
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"失败 %@", error);
}];
其中URL@"https://www.baidu.com"
可以替换成自己项目中的URL,否则如果使用百度的URL需要在AFHTTPResponseSerializer中设置ContentType,增加一个类型:text/html
。
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
AFHTTPResponseSerializer *responseSerializer = [AFHTTPResponseSerializer serializer];
NSMutableSet *setM = [NSMutableSet setWithSet:manager.responseSerializer.acceptableContentTypes];
[setM addObject:@"text/html"];
[responseSerializer setAcceptableContentTypes:setM];
[manager setResponseSerializer:responseSerializer];
[manager POST:@"https://www.baidu.com" parameters:@{} progress:^(NSProgress * _Nonnull uploadProgress) {
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"成功");
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"失败 %@", error);
}];
本篇整理AFN的初始化,也就是这句代码
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
。
AFHTTPSessionManager 部分
案例Demo的入口方法就是 AFHTTPSessionManager + manager
方法,从 + manager
方法 开始。
manager
方法
+ (instancetype)manager {
return [[[self class] alloc] initWithBaseURL:nil];
}
类方法 + manager没什么好说的,直接调用了自己的 initWithBaseURL:
方法。
initWithBaseURL:
方法
- (instancetype)initWithBaseURL:(NSURL *)url {
return [self initWithBaseURL:url sessionConfiguration:nil];
}
也没什么好说的,直接调用了自己的 initWithBaseURL:sessionConfiguration:
方法。
initWithBaseURL:sessionConfiguration:
方法
- (instancetype)initWithBaseURL:(NSURL *)url
sessionConfiguration:(NSURLSessionConfiguration *)configuration
{
self = [super initWithSessionConfiguration:configuration];
if (!self) {
return nil;
}
// Ensure terminal slash for baseURL path, so that NSURL +URLWithString:relativeToURL: works as expected
if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) {
url = [url URLByAppendingPathComponent:@""];
}
self.baseURL = url;
self.requestSerializer = [AFHTTPRequestSerializer serializer];
self.responseSerializer = [AFJSONResponseSerializer serializer];
return self;
}
概述
initWithBaseURL:sessionConfiguration:
方法的核心是 [super initWithSessionConfiguration:configuration]
。另外,self.requestSerializer = [AFHTTPRequestSerializer serializer];
和 self.responseSerializer = [AFJSONResponseSerializer serializer];
也都是重点。
整体逻辑
- 调用父类 AFURLSessionManager 的
initWithSessionConfiguration:
方法,创建一个 AFHTTPSessionManager 实例对象,用 self 接收。 - 处理入参 url 的格式(主要是斜杠
'/'
),以确保NSURL+URLWithString:relativeToURL:
可以正常工作。 - 将入参 url 保存到 AFHTTPSessionManager 的 baseURL 属性中。
- 初始化 AFHTTPRequestSerializer 并将实例对象保存到 requestSerializer 属性中。
- 初始化 AFJSONResponseSerializer 并将实例对象保存到 responseSerializer 属性中。
小结
AFHTTPSessionManager 是最上层的类,没有什么可看的,都是在调用别的类的方法为自己服务。整体逻辑1、4、5中涉及的三个重点方法解析地图:
-
initWithSessionConfiguration:
方法,转到 AFURLSessionManager 部分(往下翻)。 -
[AFHTTPRequestSerializer serializer]
,转到 AFHTTPRequestSerializer 部分(往下翻完往下翻)。 -
[AFJSONResponseSerializer serializer]
,转到 AFJSONResponseSerializer 部分(使劲往下翻)。
AFURLSessionManager 部分(高能预警!!!)
这里填第一个坑:
AFHTTPSessionManager 部分
=> initWithBaseURL:sessionConfiguration: 方法
=> 小结
=> 第1条
:转到 AFURLSessionManager 部分
最终目的是创建一个 AFHTTPSessionManager 实例对象并返回给子类。AFURLSessionManager 部分就从initWithSessionConfiguration:
方法开始。
initWithSessionConfiguration:
方法
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
self = [super init];
if (!self) {
return nil;
}
if (!configuration) {
configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
}
self.sessionConfiguration = configuration;
self.operationQueue = [[NSOperationQueue alloc] init];
self.operationQueue.maxConcurrentOperationCount = 1;
self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
self.responseSerializer = [AFJSONResponseSerializer serializer];
self.securityPolicy = [AFSecurityPolicy defaultPolicy];
#if !TARGET_OS_WATCH
self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
#endif
self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
self.lock = [[NSLock alloc] init];
self.lock.name = AFURLSessionManagerLockName;
[self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
for (NSURLSessionDataTask *task in dataTasks) {
[self addDelegateForDataTask:task uploadProgress:nil downloadProgress:nil completionHandler:nil];
}
for (NSURLSessionUploadTask *uploadTask in uploadTasks) {
[self addDelegateForUploadTask:uploadTask progress:nil completionHandler:nil];
}
for (NSURLSessionDownloadTask *downloadTask in downloadTasks) {
[self addDelegateForDownloadTask:downloadTask progress:nil destination:nil completionHandler:nil];
}
}];
return self;
}
整体逻辑
-
入参configuration如果为空,调用
[NSURLSessionConfiguration defaultSessionConfiguration]
创建 NSURLSessionConfiguration 实例对象,并保存到自己的 sessionConfiguration 属性中。 -
创建NSOperationQueue,设置最大并发数为 1,保存到 operationQueue 属性中。
-
使用参数 configuration 和 operationQueue 以及代理 self,创建 NSURLSession,保存到 session 属性中。
此处呼应AFNetworking主线梳理(二)中 AFURLSessionManager 部分的分解说明属性self.session
解析。 -
调用
[AFJSONResponseSerializer serializer]
创建 AFJSONResponseSerializer 实例对象,保存到属性responseSerializer。 -
调用
[AFSecurityPolicy defaultPolicy]
创建 AFSecurityPolicy 实例对象,保存到属性securityPolicy。- AFSecurityPolicy 本文不展开解析。
-
调用
[AFNetworkReachabilityManager sharedManager]
创建 AFNetworkReachabilityManager 实例对象,保存到属性reachabilityManager。- AFNetworkReachabilityManager 本文也不展开解析。
-
初始化可变字典属性 mutableTaskDelegatesKeyedByTaskIdentifier 。
-
创建NSLock,保存到属性lock,并设置name。
-
使用属性 session 调用
getTasksWithCompletionHandler:
方法。 -
在
getTasksWithCompletionHandler:
方法的block回调中调用addDelegateForDataTask:uploadProgress:downloadProgress:completionHandler:
方法。
分解说明
- self.session 是用来获取 dataTask 的。
呼应AFNetworking主线梳理(二)AFURLSessionManager 部分。 -
[AFJSONResponseSerializer serializer]
,详情请转到 AFJSONResponseSerializer 部分(使劲往下翻)。 - 属性 mutableTaskDelegatesKeyedByTaskIdentifier 是一个可变字典,以 taskIdentifier 为 key 存取 task。
-
getTasksWithCompletionHandler:
方法用来获取当前尚未完成的各种task。 -
getTasksWithCompletionHandler:
方法的block回调中有三个数组参数dataTasks、uploadTasks、downloadTasks,for in
逐个添加代理。注意:此block回调在子线程进行。 -
addDelegateForDataTask:uploadProgress:downloadProgress:completionHandler:
方法是一个重点,详情请继续往下看。
重点!!!
addDelegateForDataTask:uploadProgress:downloadProgress:completionHandler:
方法
addDelegateFor xxx Task方法一共有三个,服务的task类型不同,但是原理和实现是一致的。所以就以dataTask为例。
- addDelegateForDataTask
- addDelegateForUploadTask
- addDelegateForDownloadTask
- (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:dataTask];
delegate.manager = self;
delegate.completionHandler = completionHandler;
dataTask.taskDescription = self.taskDescriptionForSessionTasks;
[self setDelegate:delegate forTask:dataTask];
delegate.uploadProgressBlock = uploadProgressBlock;
delegate.downloadProgressBlock = downloadProgressBlock;
}
整体逻辑
-
使用入参 dataTask 调用
initWithTask:
方法创建 AFURLSessionManagerTaskDelegate 实例对象,用临时变量AFURLSessionManagerTaskDelegate *delegate
保存。具体请转到 AFURLSessionManagerTaskDelegate 部分。(往下翻) -
delegate保存各种配置。
-
调用
setDelegate:forTask:
函数- 以 taskIdentifier 为 key ,AFURLSessionManagerTaskDelegate 实例对象为 value,保存到属性 mutableTaskDelegatesKeyedByTaskIdentifier 中。
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate forTask:(NSURLSessionTask *)task { NSParameterAssert(task); NSParameterAssert(delegate); [self.lock lock]; self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate; [self addNotificationObserverForTask:task]; [self.lock unlock]; }
-
addNotificationObserverForTask:
方法在默认通知中心注册两个通知,暂停和开始。- self 是 AFURLSessionManager,但是作为 AFHTTPSessionManager 的父类被调用,此处 self 实际上是 AFHTTPSessionManager。
- 通知中心的 name 分别是:AFNSURLSessionTaskDidResumeNotification 和 AFNSURLSessionTaskDidSuspendNotification
- 接收通知的方法分别是:
taskDidResume:
和taskDidSuspend:
- 通知中心传递的参数 object 是入参 dataTask。
- (void)addNotificationObserverForTask:(NSURLSessionTask *)task { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidResume:) name:AFNSURLSessionTaskDidResumeNotification object:task]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidSuspend:) name:AFNSURLSessionTaskDidSuspendNotification object:task]; }
-
delegate保存传进来的 uploadProgressBlock 和 downloadProgressBlock。
问:这两个通知,都有方法来接收,那是在哪发送(post)的通知呢?
答:在 _AFURLSessionTaskSwizzling 这个私有类的af_resume
方法和af_suspend
方法中触发了通知的post。具体请转到 _AFURLSessionTaskSwizzling 部分。(往下翻)
-
taskDidResume:
方法 和taskDidSuspend:
方法
提示:下面源码中
[task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]
,这句代码中 task 的 taskDescription 属性是在 addDelegateForDataTask 方法中设置的。稍微往上翻一点就找到了。
- (NSString *)taskDescriptionForSessionTasks {
return [NSString stringWithFormat:@"%p", self];
}
- (void)taskDidResume:(NSNotification *)notification {
NSURLSessionTask *task = notification.object;
if ([task respondsToSelector:@selector(taskDescription)]) {
if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) {
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidResumeNotification object:task];
});
}
}
}
- (void)taskDidSuspend:(NSNotification *)notification {
NSURLSessionTask *task = notification.object;
if ([task respondsToSelector:@selector(taskDescription)]) {
if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) {
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidSuspendNotification object:task];
});
}
}
}
_AFURLSessionTaskSwizzling 部分
核心就在 + load 方法里:
af_resume
和af_suspend
方法是AFN自定义的方法,通过methodSwizzing交换了系统方法resume
和suspend
。也就是说调用系统的resume
和suspend
就等于调用af_resume
和af_suspend
方法。
static inline void af_swizzleSelector(Class theClass, SEL originalSelector, SEL swizzledSelector) {
Method originalMethod = class_getInstanceMethod(theClass, originalSelector);
Method swizzledMethod = class_getInstanceMethod(theClass, swizzledSelector);
method_exchangeImplementations(originalMethod, swizzledMethod);
}
static inline BOOL af_addMethod(Class theClass, SEL selector, Method method) {
return class_addMethod(theClass, selector, method_getImplementation(method), method_getTypeEncoding(method));
}
@interface _AFURLSessionTaskSwizzling : NSObject
@end
@implementation _AFURLSessionTaskSwizzling
+ (void)load {
if (NSClassFromString(@"NSURLSessionTask")) {
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
NSURLSession * session = [NSURLSession sessionWithConfiguration:configuration];
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnonnull"
NSURLSessionDataTask *localDataTask = [session dataTaskWithURL:nil];
#pragma clang diagnostic pop
IMP originalAFResumeIMP = method_getImplementation(class_getInstanceMethod([self class], @selector(af_resume)));
Class currentClass = [localDataTask class];
while (class_getInstanceMethod(currentClass, @selector(resume))) {
Class superClass = [currentClass superclass];
IMP classResumeIMP = method_getImplementation(class_getInstanceMethod(currentClass, @selector(resume)));
IMP superclassResumeIMP = method_getImplementation(class_getInstanceMethod(superClass, @selector(resume)));
if (classResumeIMP != superclassResumeIMP &&
originalAFResumeIMP != classResumeIMP) {
[self swizzleResumeAndSuspendMethodForClass:currentClass];
}
currentClass = [currentClass superclass];
}
[localDataTask cancel];
[session finishTasksAndInvalidate];
}
}
+ (void)swizzleResumeAndSuspendMethodForClass:(Class)theClass {
Method afResumeMethod = class_getInstanceMethod(self, @selector(af_resume));
Method afSuspendMethod = class_getInstanceMethod(self, @selector(af_suspend));
if (af_addMethod(theClass, @selector(af_resume), afResumeMethod)) {
af_swizzleSelector(theClass, @selector(resume), @selector(af_resume));
}
if (af_addMethod(theClass, @selector(af_suspend), afSuspendMethod)) {
af_swizzleSelector(theClass, @selector(suspend), @selector(af_suspend));
}
}
- (NSURLSessionTaskState)state {
NSAssert(NO, @"State method should never be called in the actual dummy class");
return NSURLSessionTaskStateCanceling;
}
- (void)af_resume {
NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
NSURLSessionTaskState state = [self state];
[self af_resume];
if (state != NSURLSessionTaskStateRunning) {
[[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotification object:self];
}
}
- (void)af_suspend {
NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
NSURLSessionTaskState state = [self state];
[self af_suspend];
if (state != NSURLSessionTaskStateSuspended) {
[[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidSuspendNotification object:self];
}
}
@end
分解说明
-
af_resume
和af_suspend
方法内部都有这么一句代码:NSURLSessionTaskState state = [self state];
,再看一眼 _AFURLSessionTaskSwizzling 类中对state
方法的实现,直接断言了NO
😅。
乍一看这个state
方法根本不能调用,因为内部断言NO
就会直接崩掉。所以乍一看不行,还得仔细看一看。在 + load 通过methodSwizzing交换了 NSURLSessionDataTask 和 AFN 的开始、暂停方法后,现在af_resume
和af_suspend
方法内部的 self 实际上不是 _AFURLSessionTaskSwizzling,而是NSURLSessionDataTask。
因此,调用的state
方法不是AFN的,而是 NSURLSessionDataTask 的state
方法。 -
af_resume
方法内部递归调用了af_resume
方法(af_suspend 同理)不会有问题吗?
答:一般来讲 MethodSwizzing 的常规玩法就是交换后自己调用自己,烂大街的那部分就不说了。但是,前面刚说完af_resume
和af_suspend
方法内部的 self 实际上不是 _AFURLSessionTaskSwizzling,而是NSURLSessionDataTask。NSURLSessionDataTask是系统的类,并没有af_resume
和af_suspend
方法,那么这里是不是就有问题了呢?
答:+ load 方法内部做的 MethodSwizzing 方法实现的交换调用的是AFN自定义的swizzleResumeAndSuspendMethodForClass:
函数。函数的内部实现可以看到,其入参theClass
就是 + load 里面的currentClass
。而 + load 里面的currentClass
实际上就是 NSURLSessionDataTask 类。swizzleResumeAndSuspendMethodForClass:
函数把af_resume
和af_suspend
方法添加到了 NSURLSessionDataTask 类中。这也就解释了为什么 NSURLSessionDataTask 类可以调用af_resume
和af_suspend
方法。 -
如果 NSURLSessionDataTask 不是当前状态,
state != NSURLSessionTaskStateRunning
(af_suspend 同理)就强制发送通知。
这里注意:af_resume
(af_suspend 同理)方法内部获取的 state 是在调用系统的resume
之前,一般情况下这里的 state 就是没有开始(开始就是 resume)。所以 state 不是 NSURLSessionTaskStateRunning 就意味着之前任务(task)是空闲状态,是我们调用了系统的 resume 才开启了任务(task),所以在这里发送任务开始的通知。
这里回答了 AFURLSessionManager 部分最后的那个问题:“这两个通知,都有方法来接收,那是在哪发送(post)的通知呢?”。 -
af_swizzleSelector()
函数和af_addMethod()
函数是 MethodSwizzing 的常规玩法,不再赘述。
有关 _AFURLSessionTaskSwizzling 的一些思考
-
为什么需要(会有)_AFURLSessionTaskSwizzling这个私有类?
简答:解决历史版本的遗留问题。 -
为什么要修改系统的resume和suspend方法?
简答:归根结底AFN是想在 NSURLSessionDataTask 被调用了resume
和suspend
方法时发送一次通知。 -
为什么需要通知?通知有什么用?
答:如果想使用AFNetworkingTaskDidResumeNotification来通知各种UI控件当前网络任务状态为resume,那么就得调用taskDidResume:
函数,而想要调用taskDidResume:
函数就得调用af_resume
函数。
刨根问底:_AFURLSessionTaskSwizzling
前面这一些问题都是有历史版本原因的,AFN 2.0 以及之前的版本中使用KVO监听NSURLSessionTask的state属性。并对state的状态改变做了过滤,在datatask任务暂停的时候或者开始的时候在主线程发起通知,因为有UI操作注册了当前的监听状态通知,所以需要在主线程进行操作发送状态改变的通知。
但是在使用过程中开始出现崩溃,有的是在嵌套请求网络请求的时出现了崩溃,有的是并发了多个网络请求。
为了解决KVO观察state出现的崩溃问题,后续版本开始增加了NSURLSessionDataTask的分类 (_AFStateObserving) 并在+load方法中采用methodswizzle解决此问题。
但是iOS7和iOS8中 NSURLSessionDataTask 的 api 对于resume和suspend的实现不一样,并且自iOS9一直到现在的iOS13都存在实现不一样的问题,具体如下:
iOS 7 : __NSCFLocalSessionTask、__NSCFURLSessionTask实现不一样,所以这两个都要进行交换
iOS 8 : NSURLSessionTask,只需要交换这一个类的即可
iOS 9-13 : __NSCFURLSessionTask、NSURLSessionTask,交换两个类
NSURLSessionDataTask继承关系:
iOS 7 : __NSCFLocalDataTask => __NSCFLocalSessionTask => __NSCFURLSessionTask
iOS 8 : __NSCFLocalDataTask => __NSCFLocalSessionTask => NSURLSessionTask
iOS 9-13 : __NSCFLocalDataTask => __NSCFLocalSessionTask => __NSCFURLSessionTask => NSURLSessionTask
因此为了解决以上的问题,增加了__AFURLSessionTaskSwizzling类。_AFURLSessionTaskSwizzling类使用+load做方法替换,是从__NSCFLocalDataTask开始,按照继承链向上查找。每一个实现resume的类都进行一次检查。只要和父类的resume不一样就替换成af_resume。af自己的af_resume就是加了state的判断,在调用系统的resume之前,保存state,然后强行通知DidResume。
最后的总结:
AFN最初的目的是当dataTask的State状态改变时向外部发送一个通知(Notification),一开始的写法是利用KVO观察dataTask的State状态改变。
但是KVO使用过程中出现了崩溃,就改为了MethodSwizzing。但是但是因为iOS 7、8、9...在task类的继承链中开始出现resume实现不一致,因此出现了现在的写法,按照继承链逐层向上检查。
如果有兴趣验证我的说法是否正确,可以参考AFN官方 issue 地址:
- https://github.com/AFNetworking/AFNetworking/issues/1477
- https://github.com/AFNetworking/AFNetworking/issues/2638
- https://github.com/AFNetworking/AFNetworking/pull/2702
- https://github.com/AFNetworking/AFNetworking/pull/2702
AFURLSessionManagerTaskDelegate 部分
这里填前面的坑:
- AFURLSessionManager 部分 =>
initWithSessionConfiguration:
方法 => 重点!!! =>addDelegateForDataTask:uploadProgress:downloadProgress:completionHandler:
方法 => 整体逻辑 => 第1条
前面的是说到下面的调用:
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:dataTask];
initWithTask:
方法
- (instancetype)initWithTask:(NSURLSessionTask *)task {
self = [super init];
if (!self) {
return nil;
}
_mutableData = [NSMutableData data];
_uploadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil];
_downloadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil];
__weak __typeof__(task) weakTask = task;
for (NSProgress *progress in @[ _uploadProgress, _downloadProgress ])
{
progress.totalUnitCount = NSURLSessionTransferSizeUnknown;
progress.cancellable = YES;
progress.cancellationHandler = ^{
[weakTask cancel];
};
progress.pausable = YES;
progress.pausingHandler = ^{
[weakTask suspend];
};
#if AF_CAN_USE_AT_AVAILABLE
if (@available(iOS 9, macOS 10.11, *))
#else
if ([progress respondsToSelector:@selector(setResumingHandler:)])
#endif
{
progress.resumingHandler = ^{
[weakTask resume];
};
}
[progress addObserver:self
forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
options:NSKeyValueObservingOptionNew
context:NULL];
}
return self;
}
整体逻辑
- 创建NSMutableData,保存到属性
_mutableData
- 创建NSProgress,保存到属性
_uploadProgress
- 创建NSProgress,保存到属性
_downloadProgress
-
for in
依次配置_uploadProgress
以及_downloadProgress
的属性 - 实现了
_uploadProgress
和_downloadProgress
的三个block,分别是取消(cancellationHandler)、暂停(pausingHandler)和开始(resumingHandler)。 - 在block块中使用task的弱引用调用对应的取消、暂停和重新开始方法。
- 给
_uploadProgress
和_downloadProgress
添加观察者,用来观察上传和下载的进度,delegate再以block的形式通知出去
分解说明
-
POST、GET等请求方法的
uploadProgressBlock
和downloadProgressBlock
即上传下载的block,都是由 AFURLSessionManagerTaskDelegate 保存的。AFNetworking主线梳理(二)dataTaskWithRequest
方法的解析中最后还是调用了addDelegateForDataTask
。本篇文章对addDelegateForDataTask
有详细的解析(往上翻)。 -
实现
_uploadProgress
和_downloadProgress
的三个block实际上是间接的让task去调用取消、暂停和开始。最终目的是赋予_uploadProgress
和_downloadProgress
这两个进度取消、暂停和开始的权力。 -
给
_uploadProgress
和_downloadProgress
添加观察者后,在KVO 的回调observeValueForKeyPath
方法中接收通知。 -
在
observeValueForKeyPath
方法中使用block通知开发者上传和下载的进度。
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
if ([object isEqual:self.downloadProgress]) {
if (self.downloadProgressBlock) {
self.downloadProgressBlock(object);
}
}
else if ([object isEqual:self.uploadProgress]) {
if (self.uploadProgressBlock) {
self.uploadProgressBlock(object);
}
}
}
AFJSONResponseSerializer 部分
填前面的坑:
AFHTTPSessionManager 部分
=> initWithBaseURL:sessionConfiguration: 方法
=> 小结
=> 第2条
:AFHTTPRequestSerializer 部分
解析到了 self.responseSerializer = [AFJSONResponseSerializer serializer];
这句代码,本部分就从 + serializer 方法开始。
+ serializer 方法
@implementation AFJSONResponseSerializer
+ (instancetype)serializer {
return [self serializerWithReadingOptions:(NSJSONReadingOptions)0];
}
+ (instancetype)serializerWithReadingOptions:(NSJSONReadingOptions)readingOptions {
AFJSONResponseSerializer *serializer = [[self alloc] init];
serializer.readingOptions = readingOptions;
return serializer;
}
- (instancetype)init {
self = [super init];
if (!self) {
return nil;
}
self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", nil];
return self;
}
...省略...
@end
整体逻辑
-
+ serializer
调用+ serializerWithReadingOptions:
-
+ serializerWithReadingOptions:
调用[[self alloc] init]
-
- init
调用[super init]
,调用父类 AFHTTPResponseSerializer 的- init
方法创建一个实例,并返回@implementation AFHTTPResponseSerializer - (instancetype)init { self = [super init]; if (!self) { return nil; } self.acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)]; self.acceptableContentTypes = nil; return self; }
-
设置三种默认的ContentType,并保存到acceptableContentTypes属性
"application/json"
"text/json"
"text/javascript"
-
- init
返回 serializer 实例,回到+ serializerWithReadingOptions:
-
serializer 保存 readingOptions 到属性 readingOptions
-
+ serializerWithReadingOptions:
返回 serializer 实例,回到+ serializer
-
返回 AFJSONResponseSerializer 实例对象
AFJSONResponseSerializer 以及其父类 AFHTTPRequestSerializer 的初始化很简单,只是赋了一些默认值。
AFHTTPRequestSerializer 部分
填前面的坑:
AFHTTPSessionManager 部分
=> initWithBaseURL:sessionConfiguration: 方法
=> 小结
=> 第3条
:AFJSONResponseSerializer 部分
解析到了 self.requestSerializer = [AFHTTPRequestSerializer serializer];
这句代码,本部分就从 + serializer 方法开始。
@implementation AFHTTPRequestSerializer
+ (instancetype)serializer {
return [[self alloc] init];
}
- (instancetype)init {
self = [super init];
if (!self) {
return nil;
}
// 设置默认的字符串编码
self.stringEncoding = NSUTF8StringEncoding;
// 初始化self.mutableHTTPRequestHeaders
self.mutableHTTPRequestHeaders = [NSMutableDictionary dictionary];
// 初始化self.requestHeaderModificationQueue并发队列
self.requestHeaderModificationQueue = dispatch_queue_create("requestHeaderModificationQueue", DISPATCH_QUEUE_CONCURRENT);
// 设置可接受的语言,最多接受5种语言,q是权重。"%@;q=%0.1g"是通用标准的写法。
NSMutableArray *acceptLanguagesComponents = [NSMutableArray array];
[[NSLocale preferredLanguages] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
float q = 1.0f - (idx * 0.1f);
[acceptLanguagesComponents addObject:[NSString stringWithFormat:@"%@;q=%0.1g", obj, q]];
*stop = q <= 0.5f;
}];
// 这里是第一次被赋默认值:Accept-Language
[self setValue:[acceptLanguagesComponents componentsJoinedByString:@", "] forHTTPHeaderField:@"Accept-Language"];
// 配置 userAgent
NSString *userAgent = nil;
#if TARGET_OS_IOS
// User-Agent Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43
userAgent = [NSString stringWithFormat:@"%@/%@ (%@; iOS %@; Scale/%0.2f)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[UIDevice currentDevice] model], [[UIDevice currentDevice] systemVersion], [[UIScreen mainScreen] scale]];
#elif TARGET_OS_WATCH
// User-Agent Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43
userAgent = [NSString stringWithFormat:@"%@/%@ (%@; watchOS %@; Scale/%0.2f)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[WKInterfaceDevice currentDevice] model], [[WKInterfaceDevice currentDevice] systemVersion], [[WKInterfaceDevice currentDevice] screenScale]];
#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
userAgent = [NSString stringWithFormat:@"%@/%@ (Mac OS X %@)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[NSProcessInfo processInfo] operatingSystemVersionString]];
#endif
if (userAgent) {
if (![userAgent canBeConvertedToEncoding:NSASCIIStringEncoding]) {
NSMutableString *mutableUserAgent = [userAgent mutableCopy];
if (CFStringTransform((__bridge CFMutableStringRef)(mutableUserAgent), NULL, (__bridge CFStringRef)@"Any-Latin; Latin-ASCII; [:^ASCII:] Remove", false)) {
userAgent = mutableUserAgent;
}
}
// 这里是第二次被赋默认值:User-Agent
[self setValue:userAgent forHTTPHeaderField:@"User-Agent"];
}
// 设置默认的请求方法
self.HTTPMethodsEncodingParametersInURI = [NSSet setWithObjects:@"GET", @"HEAD", @"DELETE", nil];
// 给6个特定属性添加键值观察(KVO)
self.mutableObservedChangedKeyPaths = [NSMutableSet set];
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
if ([self respondsToSelector:NSSelectorFromString(keyPath)]) {
[self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:AFHTTPRequestSerializerObserverContext];
}
}
return self;
}
整体逻辑
-
设置默认的字符串编码为
NSUTF8StringEncoding
。 -
初始化属性
self.mutableHTTPRequestHeaders
,一个可变字典。 -
初始化
self.requestHeaderModificationQueue
,一个并发队列。对于requestHeaderModificationQueue并发队列,AFNetworking主线梳理(二)AFHTTPRequestSerializer 部分 => requestWithMethod:URLString:parameters:error: 方法解析 => 第二部分 (高能预警)=> self.HTTPRequestHeaders部分 => 分解说明 => 第2、3、4条有详细的使用场景。
-
组装 Accept-Language 的信息,AFN 的逻辑是最多接受5种语言,q是语言优先的权重。
"%@;q=%0.1g"
是通用标准的格式。 -
self.mutableHTTPRequestHeaders
属性第一次设置默认值:Accept-Language。self.mutableHTTPRequestHeaders
通过setValue:forHTTPHeaderField:
方法间接赋值。// AFHTTPRequestSerializer.h - (void)setValue:(nullable NSString *)value forHTTPHeaderField:(NSString *)field; // AFHTTPRequestSerializer.m - (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field { dispatch_barrier_async(self.requestHeaderModificationQueue, ^{ [self.mutableHTTPRequestHeaders setValue:value forKey:field]; }); }
-
获取系统配置,组装 userAgent 的信息。
-
self.mutableHTTPRequestHeaders
属性第二次设置默认值:userAgent。 -
设置默认的请求方法。
使用场景在 AFNetworking主线梳理(二)AFHTTPRequestSerializer 部分 => requestWithMethod:URLString:parameters:error: 方法解析 => 第二部分 (高能预警)=> 使用query装配mutableRequest部分
-
给6个特定属性添加键值观察(KVO)。
具体请参考AFNetworking主线梳理(二)AFHTTPRequestSerializer 部分 => requestWithMethod:URLString:parameters:error: 方法解析 => 第一部分
网友评论