AFNetworking是一个非常简洁的框架,关于基本架构,可以看看这篇文章,本文主要阐述AFNetworking在设计上是如何对NSURLSession封装的。本文大致分为两个部分,第一个部分为NSURLSession的设计,第二个部分为AFNetworking的封装设计
一、NSURLSession设计
NSURLSession主要由这几个部分组成:
- NSURLSession
- NSURLSessionTask(拥有三种子类)
- NSURLSessionConfiguration
- 代理方法
首先我们通过一段Session的使用代码来看各部分之间的关系:
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; // #1
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration
delegate:self
delegateQueue:[NSOperationQueue mainQueue]]; // #2
NSURLSessionDataTask *task = [session dataTaskWithURL:[[NSURL alloc]initWithString:@""]]; // #3
[task resume];
为了方便理解这几个部分之间的关系,这段代码采用了delegate进行回调处理
- #1:创建了NSURLSessionConfiguration对象,该对象的工厂模式方法提供了三种对象
#if FOUNDATION_SWIFT_SDK_EPOCH_AT_LEAST(8)
@property (class, readonly, strong) NSURLSessionConfiguration *defaultSessionConfiguration;
@property (class, readonly, strong) NSURLSessionConfiguration *ephemeralSessionConfiguration;
#endif
+ (NSURLSessionConfiguration *)backgroundSessionConfigurationWithIdentifier:(NSString *)identifier NS_AVAILABLE(10_10, 8_0);
以上有三种方法,下面简要介绍下每种类型的特点
- defaultSessionConfiguration:默认的配置,和NSURLConnection的配置类似,使用硬盘来缓存数据(不同的是NSURLConnection的配置是全局的)
- ephemeralSessionConfiguration:不会将Cookie、缓存等存储到磁盘,而是放在内存中,程序退出时数据会消失(可以用于私密浏览)
- backgroundSessionConfigurationWithIdentifier:可以在应用程序挂起、退出、崩溃的情况下运行下载和上传任务,会在后台另外开启一个线程,但是系统会根据负载程度去调度这个线程的操作,可能会造成速度缓慢或者超时
三种工厂提供了三种对象具有不同的特点,除此之外,NSURLSessionConfiguration拥有很多的属性可以进行配置
这里列出一些常用属性:
-
@property NSTimeInterval timeoutIntervalForRequest;
:请求超时 -
@property NSTimeInterval timeoutIntervalForResource;
:资源超时 -
@property (nullable, copy) NSDictionary *HTTPAdditionalHeaders;
:请求头,配置如下
configuration.HTTPAdditionalHeaders = @{@"Accept": @"application/json",
@"Accept-Language": @"en",
@"Accept-Encoding": @"",
@"Authorization": @"",
@"Connection": @"",
@"User-Agent": @""};
可以发现字典中的key都是标准的HTTP请求头的key,可以通过这种方式对请求头进行自定义配置
-
#2:创建了NSURLSession对象,依赖于三个参数
- NSURLSessionConfiguration:配置
- Delegate:代理对象
- DelegateQueue:代理队列,在NSURLConnection中往往需要指定代理队列,代表回调方法在哪个线程中执行,NSURLSession提供了类初始化方法,省略了代理队列的指定,默认为主线程的主队列
除了这种方式之外,还可以通过单例模式提供的类方法创建,内部实现可能没有设置代理和代理队列,采用的是默认配置
@property (class, readonly, strong) NSURLSession *sharedSession;
这里的session使用了delegate回调,苹果还提供了block回调的形式:
NSURLSessionDataTask *task = [session dataTaskWithURL:[[NSURL alloc]initWithString:@""]
completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
}];
-
#3:创建NSURLSessionTask对象,NSURLSession提供了这几种task类型
- NSURLSessionTask:超类,一般不具体使用
- NSURLSessionDataTask:请求
- NSURLSessionUploadTask:上传
- NSURLSessionDownloadTask:下载
它们之间的继承关系如下:
总结:NSURLSession的设计主要有三个部分,三个部分相互独立又具有联系,NSURLSessionConfiguration进行配置管理。NSURLSession将配置,代理,代理队列等对象关联,用于创建任务。NSURLSessionTask是任务类,其对象具有操作该任务的各种方法,启动,暂停等,同时任务的回调提供两种方式,block和代理。
二、AFNetworking的封装设计
这里先回忆一下在基本架构这篇文章中所提到的AFNetworking的使用代码被分为两个部分,第一个部分是初始化AFHTTPSessionManager对象,第二个部分是调用请求方法。
先来看第一个部分,我们再次回顾一下方法的调用栈
- [AFHTTPSessionManager initWithBaseURL:]
- [AFHTTPSessionManager initWithBaseURL:sessionConfiguration:] // #1
- [AFURLSessionManager initWithSessionConfiguration:] // #2
- [NSURLSession sessionWithConfiguration:delegate:delegateQueue:]
- [AFJSONResponseSerializer serializer]
- [AFSecurityPolicy defaultPolicy]
- [AFNetworkReachabilityManager sharedManager]
- [AFHTTPRequestSerializer serializer]
- [AFJSONResponseSerializer serializer]
我们顺着方法调用栈,看方法具体实现细节,从而理解AFNetworking是如何封装NSURLSession的
- #1:
sessionConfiguration:(NSURLSessionConfiguration *)configuration
{
self = [super initWithSessionConfiguration:configuration]; // &1
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; // &2
self.requestSerializer = [AFHTTPRequestSerializer serializer];
self.responseSerializer = [AFJSONResponseSerializer serializer];
return self;
}
&1:这里调用了父类AFURLSessionManager的初始化方法
&2:设置了baseURL
- #2:
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
self = [super init];
if (!self) {
return nil;
}
if (!configuration) {
configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; // &1
}
self.sessionConfiguration = configuration;
// &2
self.operationQueue = [[NSOperationQueue alloc] init];
self.operationQueue.maxConcurrentOperationCount = 1;
self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue]; // &3
// &4
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;
// &5
[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:设置一个队列,并设置为串行(最大并发为1),在&3中创建session的时候作为参数(由于AFNetworking将AFURLSessionManager类作为了NSURLSession的代理,所以这里另外添加一个在子线程中的操作队列)
&3:创建NSURLSession对象(原生调用)
&4:设置AFNetworking中的序列化和安全策略(AF自己的模块封装)
&5:为每个task添加一个AF封装的Delegate,后文会提到
-
总结第一部分
从上面的代码细节来看,AFHTTPSessionManager的初始化操作就是做了这些事情:- 获得了NSURLSessionConfiguration对象
- 创建了NSURLSession对象,并设置自身为代理类,添加了操作队列
- 设置了AF自己封装的序列化和安全策略
这样一来,就很容易看出,其实就是在原生的初始化操作上添加了一些AF自己封装的策略对象
接下来再看看第二部分,同样回顾一下调用栈
使用GET:parameters:process:success:failure:
方法作为例子来查看一下源码实现
- [AFHTTPSessionManager GET:parameters:process:success:failure:] // #1
- [AFHTTPSessionManager dataTaskWithHTTPMethod:parameters:uploadProgress:downloadProgress:success:failure:] // #2
- [AFHTTPRequestSerializer requestWithMethod:URLString:parameters:error:]
- [AFURLSessionManager dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:] // #3
- [NSURLSession dataTaskWithRequest:]
- [AFURLSessionManager addDelegateForDataTask:uploadProgress:downloadProgress:completionHandler:] // #4
- [AFURLSessionManagerTaskDelegate init]
- [AFURLSessionManager setDelegate:forTask:] // #5
- [NSURLSessionDataTask resume]
- #1:
- (NSURLSessionDataTask *)GET:(NSString *)URLString
parameters:(id)parameters
progress:(void (^)(NSProgress * _Nonnull))downloadProgress
success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
{
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
URLString:URLString
parameters:parameters
uploadProgress:nil
downloadProgress:downloadProgress
success:success
failure:failure];
[dataTask resume];
return dataTask;
}
从表层能够看出返回了一个NSURLSessionDataTask对象,并且调用了resume操作(和原生一样),我们接着看下返回对象的方法是如何实现的
- #2:
- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
success:(void (^)(NSURLSessionDataTask *, id))success
failure:(void (^)(NSURLSessionDataTask *, NSError *))failure
{
NSError *serializationError = nil;
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError]; // &1
if (serializationError) {
if (failure) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(nil, serializationError);
});
#pragma clang diagnostic pop
}
return nil;
}
// &2
__block NSURLSessionDataTask *dataTask = nil;
dataTask = [self dataTaskWithRequest:request
uploadProgress:uploadProgress
downloadProgress:downloadProgress
completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
if (error) {
if (failure) {
failure(dataTask, error);
}
} else {
if (success) {
success(dataTask, responseObject);
}
}
}];
return dataTask;
}
&1:创建了NSURLRequest,可以发现AF内部是通过Request的方式创建的task,而不是URL
&2:调用另外一个方法返回task对象
- #3:
- (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 {
__block NSURLSessionDataTask *dataTask = nil;
url_session_manager_create_task_safely(^{
dataTask = [self.session dataTaskWithRequest:request]; // &1
});
[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler]; // &2
return dataTask;
}
&1:这部分就很熟悉了,利用原生的方式通过request来创建task对象,然后返回。到这里,就已经能明白关于task对象是如何被封装返回的
&2:按照名字来看,这里好像是为task对象添加代理方法的,我们继续往下研究
- #4:
- (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
{
// &1
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
delegate.manager = self;
delegate.completionHandler = completionHandler;
dataTask.taskDescription = self.taskDescriptionForSessionTasks;
[self setDelegate:delegate forTask:dataTask]; // &2
// &3
delegate.uploadProgressBlock = uploadProgressBlock;
delegate.downloadProgressBlock = downloadProgressBlock;
}
&1:新建了一个AFURLSessionManagerTaskDelegate对象,是AF自己封装的代理对象
&2:看样子还需要进一步查看设置代理的细节,这一个方法传入了代理对象和task对象,我们继续看
&3:将block赋值给delegate的block属性,方便回调
- #5:
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
forTask:(NSURLSessionTask *)task
{
NSParameterAssert(task);
NSParameterAssert(delegate);
[self.lock lock]; // &1
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate; // &2
[delegate setupProgressForTask:task];
[self addNotificationObserverForTask:task];
[self.lock unlock];
}
&1:有个加锁操作,保证线程安全
&2:用task的taskIdentifiier属性为key,delegate为value来进行对应,到这里,可以知道AF是用字典将delegate和task一一对应的
-
总结第二部分:
通过上面的分析,可以看出第二部分实际上就是利用NSURLRequest去创建NSURLSessionTask对象。同时呢,AFURLSessionManager作为了NSURLSession的代理,AF内部自定义了一个AFURLSessionManagerTaskDelegate代理类,该类具有很多block属性。并且,AF在内部实现了NSURLSession的代理方法,方法实现中实现block赋值,代理类的block属性对外暴露,在合适的地方回调。AF将很多NSRULSession中的代理方法都变成block形式进行暴露,更加简洁。