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:創(chuàng)建了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:可以在應用程序掛起、退出、崩潰的情況下運行下載和上傳任務,會在后臺另外開啟一個線程,但是系統(tǒng)會根據負載程度去調度這個線程的操作,可能會造成速度緩慢或者超時
三種工廠提供了三種對象具有不同的特點,除此之外,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": @""};
可以發(fā)現字典中的key都是標準的HTTP請求頭的key,可以通過這種方式對請求頭進行自定義配置
-
#2:創(chuàng)建了NSURLSession對象,依賴于三個參數
- NSURLSessionConfiguration:配置
- Delegate:代理對象
- DelegateQueue:代理隊列,在NSURLConnection中往往需要指定代理隊列,代表回調方法在哪個線程中執(zhí)行,NSURLSession提供了類初始化方法,省略了代理隊列的指定,默認為主線程的主隊列
除了這種方式之外,還可以通過單例模式提供的類方法創(chuàng)建,內部實現可能沒有設置代理和代理隊列,采用的是默認配置
@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:創(chuàng)建NSURLSessionTask對象,NSURLSession提供了這幾種task類型
- NSURLSessionTask:超類,一般不具體使用
- NSURLSessionDataTask:請求
- NSURLSessionUploadTask:上傳
- NSURLSessionDownloadTask:下載
它們之間的繼承關系如下:
總結:NSURLSession的設計主要有三個部分,三個部分相互獨立又具有聯(lián)系,NSURLSessionConfiguration進行配置管理。NSURLSession將配置,代理,代理隊列等對象關聯(lián),用于創(chuàng)建任務。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]
我們順著方法調用棧,看方法具體實現細節(jié),從而理解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:設置一個隊列,并設置為串行(最大并發(fā)為1),在&3中創(chuàng)建session的時候作為參數(由于AFNetworking將AFURLSessionManager類作為了NSURLSession的代理,所以這里另外添加一個在子線程中的操作隊列)
&3:創(chuàng)建NSURLSession對象(原生調用)
&4:設置AFNetworking中的序列化和安全策略(AF自己的模塊封裝)
&5:為每個task添加一個AF封裝的Delegate,后文會提到
-
總結第一部分
從上面的代碼細節(jié)來看,AFHTTPSessionManager的初始化操作就是做了這些事情:- 獲得了NSURLSessionConfiguration對象
- 創(chuàng)建了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:創(chuàng)建了NSURLRequest,可以發(fā)現AF內部是通過Request的方式創(chuàng)建的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來創(chuàng)建task對象,然后返回。到這里,就已經能明白關于task對象是如何被封裝返回的
&2:按照名字來看,這里好像是為task對象添加代理方法的,我們繼續(xù)往下研究
- #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:看樣子還需要進一步查看設置代理的細節(jié),這一個方法傳入了代理對象和task對象,我們繼續(xù)看
&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去創(chuàng)建NSURLSessionTask對象。同時呢,AFURLSessionManager作為了NSURLSession的代理,AF內部自定義了一個AFURLSessionManagerTaskDelegate代理類,該類具有很多block屬性。并且,AF在內部實現了NSURLSession的代理方法,方法實現中實現block賦值,代理類的block屬性對外暴露,在合適的地方回調。AF將很多NSRULSession中的代理方法都變成block形式進行暴露,更加簡潔。

