AFURLSessionManager 和 AFHTTPSessionManager 是AFNetworking的核心类 这里先阅读AFURLSessionManager。
使用 AFURLSessionManager 创建下载任务 1 2 3 4 5 6 7 8 9 10 11 12 13 NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration]; NSURL *URL = [NSURL URLWithString:@"http://example.com/download.zip" ];NSURLRequest *request = [NSURLRequest requestWithURL:URL];NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) { NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil ]; return [documentsDirectoryURL URLByAppendingPathComponent:[response suggestedFilename]]; } completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) { NSLog (@"File downloaded to: %@" , filePath); }]; [downloadTask resume];
AFURLSessionManager 初始化
initWithSessionConfiguration:
创建和管理 NSURLSession
1 2 3 4 - (instancetype )initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration{ 1. 初始化相应的属性 2. 为task设置代理 }
1. 初始化相应的属性
初始化会话配置 (NSURLSessionConfiguration),默认为defaultSessionConfiguration
初始化会话 (NSURLSession),设置会话的配置,代理以及队列
初始化响应序列化 (AFJSONResponseSerializer),安全认证 (AFSecurityPolicy),网络状态监控 (AFNetworkReachabilityManager)
初始化保存 data task 的字典(mutableTaskDelegatesKeyedByTaskIdentifier)
2. 为task设置代理
使用getTasksWithCompletionHandler
从session中取出task,并为task设置代理。
管理NSURLSessionTask
AFURLSessionManager初始化后,就可以获取NSURLSessionTask对象
AFNetworking3 中有7个方法分别返回NSURLSessionDataTask
,NSURLSessionUploadTask
,NSURLSessionDownloadTask
。这里从以下方法来看是如何实现的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromData:(NSData *)bodyData progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler { __block NSURLSessionUploadTask *uploadTask = nil ; url_session_manager_create_task_safely(^{ uploadTask = [self .session uploadTaskWithRequest:request fromData:bodyData]; }); [self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler]; return uploadTask; }
其中 url_session_manager_create_task_safely
是解决iOS8之前的一个bug ,详情可以看 Issue 2093
通过传入的NSURLRequest 生成NSURLSessionUploadTask 对象
调用自定义方法 addDelegateForUploadTask...
来为task添加回调事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - (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; }
在这个方法中 AFURLSessionManagerTaskDelegate 类实现了 NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate
代理。
使用 setDelegate
来设置代理
1 2 3 4 5 6 7 8 9 10 - (void )setDelegate:(AFURLSessionManagerTaskDelegate *)delegate forTask:(NSURLSessionTask *)task { #检查参数 [self .lock lock]; self .mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate; [self addNotificationObserverForTask:task]; [self .lock unlock]; }
AFURLSessionManager 就是通过字典 mutableTaskDelegatesKeyedByTaskIdentifier 来存储并管理每一个 NSURLSessionTask,它以 taskIdentifier 为键存储 task。 该方法使用 NSLock 来保证不同线程使用 mutableTaskDelegatesKeyedByTaskIdentifier 时,不会出现线程竞争 的问题。
实现 NSURLSessionDelegate
AFURLSessionManager初始化的时候调用 initWithSessionConfiguration,将 NSURLSession 的代理设置为self
AFURLSessionManager实现了这些协议:
NSURLSessionDelegate
NSURLSessionTaskDelegate
NSURLSessionDataDelegate
NSURLSessionDownloadDelegate
所有的delegate 实现方法都提供对应的block接口,比如:
1 2 3 4 5 6 7 8 9 10 11 #pragma mark - NSURLSessionDelegate - (void )URLSession:(NSURLSession *)session didBecomeInvalidWithError:(NSError *)error { if (self .sessionDidBecomeInvalid) { self .sessionDidBecomeInvalid(session, error); } [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDidInvalidateNotification object:session]; }
对应的block接口是:
1 - (void )setSessionDidBecomeInvalidBlock:(nullable void (^)(NSURLSession *session, NSError *error))block;
使用 AFURLSessionManagerTaskDelegate 管理进度
上文的AFURLSessionManagerTaskDelegate实现了 NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate
代理。 它主要为 task 提供进度管理功能,并在 task 结束时回调,这里开始查看如何实现的。
1 2 3 4 5 - (instancetype )initWithTask:(NSURLSessionTask *)task { 1. 设置上传或者下载任务状态改变的是回调 2. KVO }
1. 设置上传或者下载任务状态改变的是回调
1 2 3 4 5 6 7 8 9 10 11 12 13 14 progress.totalUnitCount = NSURLSessionTransferSizeUnknown ; progress.cancellable = YES ; progress.cancellationHandler = ^{ [weakTask cancel]; }; progress.pausable = YES ; progress.pausingHandler = ^{ [weakTask suspend]; }; if ([progress respondsToSelector:@selector (setResumingHandler:)]) { progress.resumingHandler = ^{ [weakTask resume]; }; }
通过对应的NSProgress 状态改变时,调用 resume
suspend
等方法改变 task 的状态
2. KVO
1 2 3 4 [progress addObserver:self forKeyPath:NSStringFromSelector (@selector (fractionCompleted)) options:NSKeyValueObservingOptionNew context:NULL ];
1 2 3 4 5 6 7 8 9 10 11 12 - (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); } } }
对progress进行键值观察,对象的某些属性改变时更新 NSProgress 对象或使用 block 传递 NSProgress 对象 self.uploadProgressBlock(object)
或者 self.downloadProgressBlock(object);
。其中fractionCompleted表示 NSProgress的进度 。
代理方法 URLSession:task:didCompleteWithError:
当task完成数据传输时会调用这个方法,接下来看一下在 AFURLSessionManagerTaskDelegate中的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 - (void )URLSession:(__unused NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { 1. 取出数据。储存responseSerializer downloadFileURL if (error){ 2. 有错误时调用completionHandler } else { 3. 无错误时调用completionHandler } }
1. 取出数据。储存responseSerializer downloadFileURL
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 __strong AFURLSessionManager *manager = self .manager; __block id responseObject = nil ; __block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer; NSData *data = nil ; if (self .mutableData) { data = [self .mutableData copy ]; self .mutableData = nil ; } if (self .downloadFileURL) { userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self .downloadFileURL; } else if (data) { userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data; }
这部分代码从 mutableData 中取出了数据,设置了 userInfo。其中 self. mutableData
是从URLSession:dataTask:didReceiveData
代理方法中获取的。userInfo 用来做通知时传送的数据
2. 3.调用completionHandler
该代理方法中的error仅指本地端的错误,比如无法解析域名或者不能连上host等。
1 2 3 4 5 6 7 8 9 dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{ if (self .completionHandler) { self .completionHandler(task.response, responseObject, error); } dispatch_async (dispatch_get_main_queue(), ^{ [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo]; }); });
如果当前 manager 持有 completionGroup 或者 completionQueue 就使用它们。否则会创建一个 dispatch_group_t 并在主线程中调用 completionHandler 并发送通知(在主线程中)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 NSError *serializationError = nil ; responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError]; if (self .downloadFileURL) { responseObject = self .downloadFileURL; } if (responseObject) { userInfo[AFNetworkingTaskDidCompleteSerializedResponseKey] = responseObject; } if (serializationError) { userInfo[AFNetworkingTaskDidCompleteErrorKey] = serializationError; }
如果没有error,先对数据进行序列化,然后同样调用 block 并发送通知。
_AFURLSessionTaskSwizzling
因为 iOS7 和 iOS8 上对于 NSURLSessionTask 的实现不同,AFURLSessionTaskSwizzling 的就是修改 NSURLSessionTask 的 resume 和 suspend 方法
使用+load 替换方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 +(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]; } }
首先用 NSClassFromString(@”NSURLSessionTask”) 判断当前部署的 iOS 版本是否含有类 NSURLSessionTask
因为 iOS7 和 iOS8 上对于 NSURLSessionTask 的实现不同,所以会通过 - [NSURLSession dataTaskWithURL:] 方法返回一个 NSURLSessionTask 实例
取得当前类 _AFURLSessionTaskSwizzling 中的实现 af_resume
如果当前类 currentClass 有 resume 方法
使用 swizzleResumeAndSuspendMethodForClass: 调剂该类的 resume 和 suspend 方法
currentClass = [currentClass superclass]
使用 AFSecurityPolicy 保证请求的安全 AFSecurityPolicy 具体参考 AFNetworking3源码阅读-Security
在代理方法 URLSession:didReceiveChallenge:completionHandler
中调用
1 2 3 ... if ([self .securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {...