1 概述
AFNetWorking
基本上是所有iOS项目的标配。现在升级带最新版的3.X了。得益于苹果从NSURLConnection
升级到NSURLSession
,AFN也实现了api的简化,同时功能却一点没少。我们来看一下AFN3.X的目录结构:
- AFNetWorking 这个文件是一个头文件。啥也没做,就是引入了其他文件方便使用。
- AFURLSessionManager 这个文件是核心类,基本上通过它来实现了大部分核心功能。负责请求的建立、管理、销毁、安全、请求重定向、请求重启等各种功能。他主要实现了
NSURLSession
和NSRULSessionTask
的封装。
- AFHTTPSessionManager 这个文件是
AFURLSessionManager
的子类。主要实现了对HTTP请求的优化。
- AFURLRequestSerialization 这个主要用于请求头的编码解码、序列化、优化处理、简化请求拼接过程等。
- AFURLResponseSerialization 这个主要用于网络返回数据的序列化、编码解码、序列化、数据处理等。
- AFSecurityPolicy 这个主要用于请求的认证功能。比如https的认证模式等。
- AFNetworkReachabilityManager 这个主要用于监听网络请求状态变化功能。
首先说明,看AFN源码之前一定要搞清楚NSURLSession
系列的api,这样能让你事半功倍,具体可以看AFNetWorking源码之NSRULSession系列概述。在这篇文章里,我们主要讲解AFURLSessionManager
的实现原理和封装过程。首先我们通过一个简单的网络请求看一下他的基本用法(大部分都是非必须的,这里为了掩饰写出来):
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| - (IBAction)clickButton:(id)sender { NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration]; AFHTTPRequestSerializer *requestSerializer = [AFHTTPRequestSerializer serializer]; [requestSerializer setValue:@"test" forHTTPHeaderField:@"requestHeader"]; requestSerializer.timeoutInterval = 60; requestSerializer.stringEncoding = NSUTF8StringEncoding; AFHTTPResponseSerializer *responseSerializer = [AFHTTPResponseSerializer serializer]; manager.responseSerializer = responseSerializer; if (true) { AFSecurityPolicy *securityPolicy; securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModePublicKey]; securityPolicy.allowInvalidCertificates = false; securityPolicy.validatesDomainName = YES; manager.securityPolicy = securityPolicy; } else { manager.securityPolicy.allowInvalidCertificates = true; manager.securityPolicy.validatesDomainName = false; } if (true) { [manager setTaskWillPerformHTTPRedirectionBlock:^NSURLRequest *(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request) { if (response) { return nil; } return request; }]; } [manager.reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) { NSLog(@"%ld",(long)status); }]; [manager.reachabilityManager startMonitoring]; NSURL *URL = [NSURL URLWithString:bigPic]; NSURLRequest *request = [NSURLRequest requestWithURL:URL]; NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:^(NSProgress *downloadProgress){ NSLog(@"下载进度:%lld",downloadProgress.completedUnitCount); } destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) { NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil]; NSURL *fileURL = [documentsDirectoryURL URLByAppendingPathComponent:[response suggestedFilename]]; NSLog(@"fileURL:%@",[fileURL absoluteString]); return fileURL; } completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) { self.imageView.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:filePath]]; NSLog(@"File downloaded to: %@", filePath); }]; [downloadTask resume]; }
|
通过这个请求,我们发现AFURLSessionManager
要负责以下几块功能。
- 初始化和管理
NSURLSession
,通过它来建立和管理各种Task。
- 初始化和管理
NSRULSessionTask
,通过不同task来发送不同请求。
- 管理各种认证功能、安全功能、请求重定向、数据处理。
- 管理和组织每个task的各种状态管理和通知管理。不同task的回调处理。
- 帮我们管理和处理了
NSRULSession
系列api的各种代理方法。简化了我们的处理。
2 AFURLSessionManager的声明分析
AFURLSessionManager
根据一个指定的NSURLSessionConfiguration
创建和管理一个NSURLSession
对象。并且这个对象实现了<NSURLSessionTaskDelegate>
, <NSURLSessionDataDelegate>
, <NSURLSessionDownloadDelegate>
, 和 <NSURLSessionDelegate>
这几个协议的协议方法。同时实现NSSecureCoding
和NSCopying
来实现归档解档和copy功能。
2.1 AFURLSessionManager
的初始化api
这些api主要用于初始化、安全策略、网络状态监听等:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| interface AFURLSessionManager : NSObject <NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate, NSSecureCoding, NSCopying>
- (instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)configuration
@property (readonly, nonatomic, strong) NSURLSession *session;
@property (readonly, nonatomic, strong) NSOperationQueue *operationQueue;
@property (nonatomic, strong) id <AFURLResponseSerialization> responseSerializer;
@property (nonatomic, strong) AFSecurityPolicy *securityPolicy;
@property (readwrite, nonatomic, strong) AFNetworkReachabilityManager *reachabilityManager; @end
|
2.2 AFURLSessionManager
获取Task的api
这部分api主要是任务的创建、任务的分类、任务完成队列处理、特殊情况的任务重新创建等:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @property (readonly, nonatomic, strong) NSArray <NSURLSessionTask *> *tasks;
@property (readonly, nonatomic, strong) NSArray <NSURLSessionDataTask *> *dataTasks;
@property (readonly, nonatomic, strong) NSArray <NSURLSessionUploadTask *> *uploadTasks;
@property (readonly, nonatomic, strong) NSArray <NSURLSessionDownloadTask *> *downloadTasks;
@property (nonatomic, strong, nullable) dispatch_queue_t completionQueue;
@property (nonatomic, strong, nullable) dispatch_group_t completionGroup;
@property (nonatomic, assign) BOOL attemptsToRecreateUploadTasksForBackgroundSessions;
- (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks;
|
2.3 AFURLSessionManager
为管理Task创建Block
AFURLSessionManager
提供了很多创建Task的api。并且提供了很多处理Task的Block。应该说着几个api就是AFN为我们提供的最大价值,他把所有delegate方法细节都处理好。直接提供给我们一些最实用的api,我们就不用去管理session系列繁琐的delegate方法了。
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 28 29 30 31 32 33 34 35 36 37
| - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler;
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request uploadProgress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock downloadProgress:(nullable void (^)(NSProgress *downloadProgress))downloadProgressBlock completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler;
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler;
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromData:(nullable NSData *)bodyData progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler;
- (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler;
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgressBlock destination:(nullable NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination completionHandler:(nullable void (^)(NSURLResponse *response, NSURL * _Nullable filePath, NSError * _Nullable error))completionHandler;
- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgressBlock destination:(nullable NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination completionHandler:(nullable void (^)(NSURLResponse *response, NSURL * _Nullable filePath, NSError * _Nullable error))completionHandler;
- (nullable NSProgress *)uploadProgressForTask:(NSURLSessionTask *)task;
- (nullable NSProgress *)downloadProgressForTask:(NSURLSessionTask *)task;
|
注意:上面所有Task的progress都不在主线程、所以要在progress中做UI更新,都必须手动在主线程操作。
2.4 AFURLSessionManager
设置各种情况的代理回调
这些回调Block主要是用于处理网络请求过程或者结束以后的数据处理、认证、通知、缓存等。我们可以通过设置这些Block来获取或者检测各种状态。相当于就是钩子函数。通过下面的这些Block,我们基本可以获取请求过程中的所有状态以及需要做的各种处理。
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| - (void)setSessionDidBecomeInvalidBlock:(nullable void (^)(NSURLSession *session, NSError *error))block{ }
- (void)setSessionDidReceiveAuthenticationChallengeBlock:(nullable NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * _Nullable __autoreleasing * _Nullable credential))block{ }
- (void)setTaskDidReceiveAuthenticationChallengeBlock:(nullable NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * _Nullable __autoreleasing * _Nullable credential))block{ }
- (void)setTaskNeedNewBodyStreamBlock:(nullable NSInputStream * (^)(NSURLSession *session, NSURLSessionTask *task))block{ }
- (void)setTaskWillPerformHTTPRedirectionBlock:(nullable NSURLRequest * (^)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request))block{ }
- (void)setTaskDidSendBodyDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend))block{ }
- (void)setTaskDidCompleteBlock:(nullable void (^)(NSURLSession *session, NSURLSessionTask *task, NSError * _Nullable error))block{ }
- (void)setDataTaskDidReceiveResponseBlock:(nullable NSURLSessionResponseDisposition (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response))block{ }
- (void)setDataTaskDidBecomeDownloadTaskBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask))block{ }
- (void)setDataTaskDidReceiveDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data))block{ }
- (void)setDataTaskWillCacheResponseBlock:(nullable NSCachedURLResponse * (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse))block{ }
- (void)setDidFinishEventsForBackgroundURLSessionBlock:(nullable void (^)(NSURLSession *session))block{ }
- (void)setDownloadTaskDidFinishDownloadingBlock:(nullable NSURL * _Nullable (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location))block{ }
- (void)setDownloadTaskDidWriteDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite))block{ }
- (void)setDownloadTaskDidResumeBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes))block{ }
|
除了上面的部分,AFURLSessionManager
的头文件还提供了很多notification
的声明。通过这些通知,我们可以获取Task是否开始、是否完成、是否挂起、是否无效等各种通知。具体可以去文件里看。
3 AFURLSessionManager的实现分析
AFURLSessionManager.m
文件里面除了有AFURLSessionManager.h
定义的各种接口的实现意外,还有处理不同iOS版本下NSRULSession
不同的部分,以及多个全局dispatch_queue_t
的定义、以及处理NSURLSeesionTash
的各种代理方法的实现和处理。具体划分如下:
NSURLSessionManager
的实现。主要实现了接口文件定义的各种api的实现,比如Task的创建、Task的获取、Task的各种代理方法的实现、NSCoping和NSCoding协议、以及各种Block的实现。
- 基本属性的初始化。比如
sessionConfiguration
、operationQueue
、session
、mutableTaskDelegatesKeyedByTaskIdentifier
等属性。以及用于实现task和AFURLSessionManagerTaskDelegate
的绑定的taskDescriptionForSessionTasks
、还有关键操作的锁属性lock。
- 接口文件的各种Block对应的属性,一个Block对应一个属性。
- 处理Task暂停与重启操作的方法。
- 给Task设置
AFURLSessionManagerTaskDelegate
代理的方法。
- 初始化Task的各种方法。
- 设置B接口文件定义的各种Block。
NSURLSession
系列代理方法。
_AFURLSessionTaskSwizzling
私有类。主要实现了iOS7和iOS8系统上NSURLSession
差别的处理。让不同系统版本NSURLSession
版本基本一致。
AFURLSessionManagerTaskDelegate
这个类主要是把NSURLSeesion
的部分代理方法让他处理。从而达到简化代码的目的。
- 处理Task的上传或者下载进度。
- 处理封装
NSURLSeesion
返回的数据。
- Task完成等的通知封装。
- 全局
dispatch_queue_t
和dispatch_group_t
的定义。各种通知名称的初始化,各种Block的类型定义。
3.1 AFURLSessionManager一个网络请求实现过程
我们通过一个网络请求过程来分析AFURLSessionManager.m
的实现。我们通过initWithSessionConfiguration
方法初始化一个manager。在这个方法里会初始化各种属性、以及为session属性设置代理:
接口文件中的代码如下:
1
| AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
|
实现文件中对应的处理如下:
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
|
- (instancetype)init { return [self initWithSessionConfiguration:nil]; }
- (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; }
|
请求执行,接口文件如下:
1 2 3 4 5 6 7 8 9 10 11
| NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:^(NSProgress *downloadProgress){ NSLog(@"下载进度:%lld",downloadProgress.completedUnitCount); } destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) { NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil]; NSURL *fileURL = [documentsDirectoryURL URLByAppendingPathComponent:[response suggestedFilename]]; NSLog(@"fileURL:%@",[fileURL absoluteString]); return fileURL; } completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) { self.imageView.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:filePath]]; NSLog(@"File downloaded to: %@", filePath); }];
|
实现文件则调用了很多方法:
1 首先是初始化一个NSURLSessionDownLoadTask
对象
1 2 3 4 5 6 7 8
| __block NSURLSessionDownloadTask *downloadTask = nil;
url_session_manager_create_task_safely(^{ downloadTask = [self.session downloadTaskWithRequest:request]; }); [self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler]; return downloadTask;
|
2 通过[self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler];
这句话来为Task设置一个AFURLSessionManagerTaskDelegate
代理对象。从而可以实现对进度处理、Block调用、Task完成返回数据的拼装的功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:downloadTask]; delegate.manager = self;
delegate.completionHandler = completionHandler; if (destination) { delegate.downloadTaskDidFinishDownloading = ^NSURL * (NSURLSession * __unused session, NSURLSessionDownloadTask *task, NSURL *location) { return destination(location, task.response); }; }
downloadTask.taskDescription = self.taskDescriptionForSessionTasks;
[self setDelegate:delegate forTask:downloadTask];
delegate.downloadProgressBlock = downloadProgressBlock;
|
3 初始化一个AFURLSessionManagerTaskDelegate对象。在这个对象中对Task的请求过程进行处理和控制。
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
|
- (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 ([progress respondsToSelector:@selector(setResumingHandler:)]) { progress.resumingHandler = ^{ [weakTask resume]; }; } [progress addObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted)) options:NSKeyValueObservingOptionNew context:NULL]; } return self; }
- (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); } } }
|
4 在AFURLSessionManagerTaskDelegate
设置Task状态改变的监听。
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)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate forTask:(NSURLSessionTask *)task { NSParameterAssert(task); NSParameterAssert(delegate); [self.lock lock]; self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate; [self addNotificationObserverForTask:task]; [self.lock unlock]; }
- (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]; }
|
5 从下面开始,任务就正式开始执行。其实就是[downloadTask resume];
执行以后开始。
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 28 29 30 31 32 33 34 35
|
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite { AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask]; if (delegate) { [delegate URLSession:session downloadTask:downloadTask didWriteData:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite]; } if (self.downloadTaskDidWriteData) { self.downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite); } }
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{ self.downloadProgress.totalUnitCount = totalBytesExpectedToWrite; self.downloadProgress.completedUnitCount = totalBytesWritten; }
|
6 Task完成以后,会调用AFURLSessionManagerTaskDelegate
对象的方法对返回的数据封装。
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| - (void)URLSession:(__unused NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { __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; } if (error) { userInfo[AFNetworkingTaskDidCompleteErrorKey] = error; 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]; }); }); } else { dispatch_async(url_session_manager_processing_queue(), ^{ 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; } dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{ if (self.completionHandler) { self.completionHandler(task.response, responseObject, serializationError); } dispatch_async(dispatch_get_main_queue(), ^{ [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo]; }); }); }); } }
|
7 移除Task对应的通知和对应的AFURLSessionManagerTaskDelegate
代理对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| - (void)removeDelegateForTask:(NSURLSessionTask *)task { NSParameterAssert(task); [self.lock lock]; [self removeNotificationObserverForTask:task]; [self.mutableTaskDelegatesKeyedByTaskIdentifier removeObjectForKey:@(task.taskIdentifier)]; [self.lock unlock]; }
- (void)removeNotificationObserverForTask:(NSURLSessionTask *)task { [[NSNotificationCenter defaultCenter] removeObserver:self name:AFNSURLSessionTaskDidSuspendNotification object:task]; [[NSNotificationCenter defaultCenter] removeObserver:self name:AFNSURLSessionTaskDidResumeNotification object:task]; }
- (void)dealloc { [self.downloadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))]; [self.uploadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))]; }
|
通过上面的过程,我们发现核心流程都是围绕了NSRULSessionTask
对象以及与之绑定的AFURLSessionManagerTaskDelegate
对象执行的。我们通过在NSRULSessionTask
对象的代理方法里面手动调用AFURLSessionManagerTaskDelegate
对应的代理方法来实现对数据的处理和简化代码的作用,这个设计思路的确吊吊的。还有一些方法没有涉及到,不过大同小异,基本过程就是这样,就不一一解释了。
3.2 AFURLSessionManager一些特殊模块的说明
AFURLSeeesionManager
实现了NSSecureCoding
协议。让manager可以归档解档。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
+ (BOOL)supportsSecureCoding { return YES; }
- (instancetype)initWithCoder:(NSCoder *)decoder { NSURLSessionConfiguration *configuration = [decoder decodeObjectOfClass:[NSURLSessionConfiguration class] forKey:@"sessionConfiguration"]; self = [self initWithSessionConfiguration:configuration]; if (!self) { return nil; } return self; }
- (void)encodeWithCoder:(NSCoder *)coder { [coder encodeObject:self.session.configuration forKey:@"sessionConfiguration"]; }
|
同时,AFURLSessionManager
也实现了NSCopying
协议。通过协议的实现过程,我们发现也是只使用了NSURLSessionConfiguration
属性。和归档解档一样。
1 2 3 4
| #pragma mark - 实现NSCopying协议。copy的NAURLSessionManager没有复制任何与代理处理相关的Block - (instancetype)copyWithZone:(NSZone *)zone { return [[[self class] allocWithZone:zone] initWithSessionConfiguration:self.session.configuration]; }
|
有的时候,我们的请求会返回302这个状态码,这个表示需要请求重定向到另一个url,我们可以下面这个代理方法里面决定对于重定向的处理,如果对completionHandler
传入nil,则会把response传入重定向请求。另外,backgroundSession的Task不会调用下面这个代理方法,而是直接调用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest *))completionHandler { NSURLRequest *redirectRequest = request; if (self.taskWillPerformHTTPRedirection) { redirectRequest = self.taskWillPerformHTTPRedirection(session, task, response, request); } if (completionHandler) { completionHandler(redirectRequest); } }
|
创建NSRULSessionUplaodTask
的时候,在某些系统上会出现bug。AFN已经帮我们处理好:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL 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 fromFile:fileURL]; }); if (!uploadTask && self.attemptsToRecreateUploadTasksForBackgroundSessions && self.session.configuration.identifier) { for (NSUInteger attempts = 0; !uploadTask && attempts < AFMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask; attempts++) { uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL]; } } [self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler]; return uploadTask; }
|
通过使用dispatch_semaphore_t
来控制对异步处理返回结果的控制。非常有借鉴意义。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #pragma mark - 获取当前session对应的task列表。通过dispatch_semaphore_t来控制访问过程。 - (NSArray *)tasksForKeyPath:(NSString *)keyPath { __block NSArray *tasks = nil; dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) { if ([keyPath isEqualToString:NSStringFromSelector(@selector(dataTasks))]) { tasks = dataTasks; } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(uploadTasks))]) { tasks = uploadTasks; } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(downloadTasks))]) { tasks = downloadTasks; } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(tasks))]) { tasks = [@[dataTasks, uploadTasks, downloadTasks] valueForKeyPath:@"@unionOfArrays.self"]; } dispatch_semaphore_signal(semaphore); }]; dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); return tasks; }
|
4 _AFURLSessionTaskSwizzling私有类的说明
在iOS7和iOS8及以上的系统,NSRULSessionTask
的具体实现是不同的。我们目前知道的不同有:
NSURLSessionTasks
是一个类簇。所以我们初始化一个Task的时候,我们并不只到初始化的到底是哪个子类。
- 简单的通过
[NSURLSessionTask class]
并不会起作用。必须通过NSURLSession
创建一个task对象。然后获取他所在的类。
- iOS7下面,下面代码中的
localDataTask
对象的继承关系是__NSCFLocalDataTask
-> __NSCFLocalSessionTask
-> __NSCFURLSessionTask
。
- 在iOS8以及以上系统。下面代码中的
localDataTask
对象的继承关系是__NSCFLocalDataTask
-> __NSCFLocalSessionTask
-> NSURLSessionTask
。
- 在iOS7下面
__NSCFLocalSessionTask
和__NSCFURLSessionTask
实现了resume
和suspend
方法,同时最重要的是他不调用父类的实现。但是iOS8下面,只有NSURLSessionTask
实现了resume
和suspend
。所以在iOS7的环境下,我们需要想办法让resume
和suspend
调用NSURLSessionTask
的具体实现。
下面的代码完美的向我们展示了一个向类添加方法,并且swizzle方法实现的过程。值得仔细琢磨。
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
|
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)); } @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
|
5 总结
AFURLSessionManager
通过对task设置一个AFURLSessionManagerTaskDelegate
代理来处理繁杂的请求进度管理。从而降低了代码的负责度。是代理模式的一个很好的实践。
AFURLSessionManager
通过私有类_AFURLSessionTaskSwizzling
来修改iOS7和iOS8系统上面不同。是对于方法swizzle的一个成功和完整的实践。
AFURLSessionManager
通过添加各种Block,让我们对请求过程有全方位的控制和处理。同时提供简洁的api,把负责的处理全部封装好。
具体源码在iOSSourceCodeStudy。