該文章閱讀的AFNetworking的版本為3.2.0。
1.初始化
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
複製程式碼
在AFHTTPSessionManager
類中所有的初始化方法最終都會呼叫到- (instancetype)initWithBaseURL:(NSURL *)url sessionConfiguration:(NSURLSessionConfiguration *)configuration
方法中
- (instancetype)initWithBaseURL:(NSURL *)url
sessionConfiguration:(NSURLSessionConfiguration *)configuration
{
// 首先呼叫了父類AFURLSessionManager的初始化方法
self = [super initWithSessionConfiguration:configuration];
if (!self) {
return nil;
}
// 如果有效的url後沒有正斜線“/”就新增上正斜線“/”
if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) {
url = [url URLByAppendingPathComponent:@""];
}
// 記錄url
self.baseURL = url;
// 例項化請求和響應序列化物件
self.requestSerializer = [AFHTTPRequestSerializer serializer];
self.responseSerializer = [AFJSONResponseSerializer serializer];
return self;
}
複製程式碼
在其父類AFURLSessionManager
類中的- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration
中做了基本屬性的初始化
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
self = [super init];
if (!self) {
return nil;
}
// 初始化NSURLSessionConfiguration如果沒有傳則用預設
if (!configuration) {
configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
}
self.sessionConfiguration = configuration;
// 初始化一個操作佇列並設定最大執行緒併發數為1
self.operationQueue = [[NSOperationQueue alloc] init];
self.operationQueue.maxConcurrentOperationCount = 1;
// 利用已經初始化好的NSURLSessionConfiguration和NSOperationQueue初始化NSURLSession並遵守了代理
self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
// 初始化JSON響應序列物件
self.responseSerializer = [AFJSONResponseSerializer serializer];
// 初始化安全策略
self.securityPolicy = [AFSecurityPolicy defaultPolicy];
// 初始化網路狀態監測物件
#if !TARGET_OS_WATCH
self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
#endif
// 初始化可變字典儲存NSURLSessionTask和其對應的delegate,並以NSURLSessionTask的屬性taskIdentifier做key,以NSURLSession的delegate做value
self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
// 初始化self.mutableTaskDelegatesKeyedByTaskIdentifier可變字典的鎖,確保字典在多執行緒訪問時的執行緒安全
self.lock = [[NSLock alloc] init];
self.lock.name = AFURLSessionManagerLockName;
// 非同步的獲取當前session的所有未完成的task,並進行置空處理,主要是為了從後臺切換到前臺時重新初始化session
[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;
}
複製程式碼
2.進行網路請求
以GET請求為例
[manager GET:URLString parameters:parameters progress:^(NSProgress * _Nonnull downloadProgress) {
NSLog(@"下載進度:%@", downloadProgress);
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"響應物件:%@", responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"錯誤:%@", error);
}];
複製程式碼
點選進入方法中
- (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物件
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
URLString:URLString
parameters:parameters
uploadProgress:nil
downloadProgress:downloadProgress
success:success
failure:failure];
// 開始任務
[dataTask resume];
return dataTask;
}
複製程式碼
發現所有的請求方法無論是GET、POST還是其他都呼叫了同一個方法
- (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
{
// 把所有的引數解析拼接生成一個NSMutableURLRequest物件,如果無法解析則回撥返回錯誤
NSError *serializationError = nil;
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
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;
}
// 用生成的NSMutableURLRequest物件生成一個NSURLSessionDataTask物件並回撥成功或失敗
__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;
}
複製程式碼
2.1生成NSURLRequest物件
先看在AFURLRequestSerialization
類中如何利用各種引數生成NSMutableURLRequest
物件
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
// 在debug模式下如果缺少引數則會crash
NSParameterAssert(method);
NSParameterAssert(URLString);
NSURL *url = [NSURL URLWithString:URLString];
NSParameterAssert(url);
// 生成NSMutableURLRequest物件並設定請求方式
NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
mutableRequest.HTTPMethod = method;
// 遍歷AFHTTPRequestSerializer的各個屬性
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
// 如果發現有正在被觀察的屬性
if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
// 把本類對應屬性的值賦給NSMutableURLRequest對應的屬性
[mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
}
}
// 將傳入的parameters新增到mutableRequest中
mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];
return mutableRequest;
}
複製程式碼
- 首先,在這個方法中有一個C語言函式
static NSArray *AFHTTPRequestSerializerObservedKeyPaths()
這個函式實際上是將AFHTTPRequestSerializer
類中一些屬性的名字封裝成字串並以陣列形式返回 - 其次,
self.mutableObservedChangedKeyPaths
這個屬性在AFURLRequestSerialization
的初始化方法- (instancetype)init
中進行了初始化
self.mutableObservedChangedKeyPaths = [NSMutableSet set];
// 觀察AFHTTPRequestSerializerObservedKeyPaths()函式返回的屬性
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
if ([self respondsToSelector:NSSelectorFromString(keyPath)]) {
[self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:AFHTTPRequestSerializerObserverContext];
}
}
複製程式碼
並在KVO方法中進行賦值
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(__unused id)object
change:(NSDictionary *)change
context:(void *)context
{
if (context == AFHTTPRequestSerializerObserverContext) {
// 如果給當前屬性賦的值不為null就新增到self.mutableObservedChangedKeyPaths中,否則從其中移除
if ([change[NSKeyValueChangeNewKey] isEqual:[NSNull null]]) {
[self.mutableObservedChangedKeyPaths removeObject:keyPath];
} else {
[self.mutableObservedChangedKeyPaths addObject:keyPath];
}
}
}
複製程式碼
由此所以我們可以得知self.mutableObservedChangedKeyPaths
中儲存的就是我們設定的AFHTTPRequestSerializer
物件的屬性的集合
- 最後,呼叫方法
[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error]
把parameters
編碼並設定到mutableRequest中
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
// 在debug模式下如果缺少NSURLRequest物件則會crash
NSParameterAssert(request);
NSMutableURLRequest *mutableRequest = [request mutableCopy];
// 遍歷並對request沒有的屬性進行賦值
[self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
if (![request valueForHTTPHeaderField:field]) {
[mutableRequest setValue:value forHTTPHeaderField:field];
}
}];
// 把parameters編碼成字串
NSString *query = nil;
if (parameters) {
// 如果自定義了引數編碼方式
if (self.queryStringSerialization) {
NSError *serializationError;
// 使用者可通過block自定義引數的編碼方式
query = self.queryStringSerialization(request, parameters, &serializationError);
if (serializationError) {
if (error) {
*error = serializationError;
}
return nil;
}
// 使用預設的引數編碼方式
} else {
switch (self.queryStringSerializationStyle) {
case AFHTTPRequestQueryStringDefaultStyle:
query = AFQueryStringFromParameters(parameters);
break;
}
}
}
// 判斷是否是GET、HEAD、DELETE請求,self.HTTPMethodsEncodingParametersInURI這個屬性在AFURLRequestSerialization的初始化方法- (instancetype)init中進行了初始化
if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
if (query && query.length > 0) {
// 將編碼好的引數拼接在url後面
mutableRequest.URL = [NSURL URLWithString:[[mutableRequest.URL absoluteString] stringByAppendingFormat:mutableRequest.URL.query ? @"&%@" : @"?%@", query]];
}
// 如果是POST、PUT請求
} else {
// #2864: an empty string is a valid x-www-form-urlencoded payload
if (!query) {
query = @"";
}
if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
[mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
}
// 把編碼好的引數拼到http的body中
[mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
}
return mutableRequest;
}
複製程式碼
大概的看一下AFNetworking
預設的的引數編碼方式,AFQueryStringFromParameters()
點進去以後可以看到是三個方法
NSString * AFQueryStringFromParameters(NSDictionary *parameters) {
NSMutableArray *mutablePairs = [NSMutableArray array];
// 遍歷由集合物件處理成AFQueryStringPair元素組成的陣列
for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
// 把AFQueryStringPair元素的屬性拼接成字串新增到mutablePairs中,如果有value值就拼接成“field=value”的形式,否則為“field”
[mutablePairs addObject:[pair URLEncodedStringValue]];
}
// 把mutablePairs中的字串用&連結成一個字串
return [mutablePairs componentsJoinedByString:@"&"];
}
NSArray * AFQueryStringPairsFromDictionary(NSDictionary *dictionary) {
return AFQueryStringPairsFromKeyAndValue(nil, dictionary);
}
NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) {
NSMutableArray *mutableQueryStringComponents = [NSMutableArray array];
// 設定排序描述為按照物件的description屬性升序排列
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(compare:)];
// 如果value是NSDictionary
if ([value isKindOfClass:[NSDictionary class]]) {
// 將NSDictionary的key按照首字母升序排列後遍歷出nestedKey及其對應的nestedValue,然後遞迴呼叫AFQueryStringPairsFromKeyAndValue()方法,如果有key值則傳(key[nestedKey], nestedValue),否則傳(nestedKey, nestedValue)
NSDictionary *dictionary = value;
for (id nestedKey in [dictionary.allKeys sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
id nestedValue = dictionary[nestedKey];
if (nestedValue) {
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)];
}
}
// 如果value是NSArray
} else if ([value isKindOfClass:[NSArray class]]) {
// 直接遍歷取出nestedValue,然後遞迴呼叫AFQueryStringPairsFromKeyAndValue()方法,如果有key值則傳遞(key[], nestedValue),否則傳((null)[], nestedValue)
NSArray *array = value;
for (id nestedValue in array) {
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)];
}
// 如果value是NSSet
} else if ([value isKindOfClass:[NSSet class]]) {
// 將NSSet的值按照首字母升序排列後遍歷出值obj,然後遞迴呼叫AFQueryStringPairsFromKeyAndValue()方法,如果有key值則傳(key, obj),否則傳((null), obj)
NSSet *set = value;
for (id obj in [set sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue(key, obj)];
}
// 如果value不是集合物件
} else {
例項化AFQueryStringPair物件新增到mutableQueryStringComponents陣列中,也就是說AFQueryStringPairsFromKeyAndValue()這個方法執行結束後,返回的是由集合物件轉化為AFQueryStringPair物件的元素組成的陣列
[mutableQueryStringComponents addObject:[[AFQueryStringPair alloc] initWithField:key value:value]];
}
return mutableQueryStringComponents;
}
複製程式碼
至此,例項化NSMutableURLRequest
物件的任務就結束了,然後就需要再回到- (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
這個方法中,利用上一步例項化好的NSURLSessionDataTask
物件request
生成網路請求任務NSURLSessionDataTask
2.2 生成NSURLSessionTask物件
- (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];
});
[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
return dataTask;
}
複製程式碼
發現dataTask並不是直接建立,而是在一個block中建立,點進url_session_manager_create_task_safely
可以看到這其實是做了一個版本的相容
static void url_session_manager_create_task_safely(dispatch_block_t block) {
if (NSFoundationVersionNumber < NSFoundationVersionNumber_With_Fixed_5871104061079552_bug) {
// Fix of bug
// Open Radar:http://openradar.appspot.com/radar?id=5871104061079552 (status: Fixed in iOS8)
// Issue about:https://github.com/AFNetworking/AFNetworking/issues/2093
// 蘋果在iOS8的時候已經解決了這個bug,這是為了相容iOS8之前
dispatch_sync(url_session_manager_creation_queue(), block);
} else {
block();
}
}
複製程式碼
這裡作者自定義了一個序列佇列,並且只生成一次
static dispatch_queue_t url_session_manager_creation_queue() {
static dispatch_queue_t af_url_session_manager_creation_queue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
af_url_session_manager_creation_queue = dispatch_queue_create("com.alamofire.networking.session.manager.creation", DISPATCH_QUEUE_SERIAL);
});
return af_url_session_manager_creation_queue;
}
複製程式碼
因此,可以發現如果是在iOS8之前的版本建立dataTask
是在一個同步序列佇列裡,通過查詢資料,可以知道在iOS8之前- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request;
這個方法是非同步併發執行的,所以會出現實際回撥completionHandlers
並不是當初task
的回撥,而是另一個task
的回撥。
接下來是對生成的dataTask
新增代理
- (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的物件並和NSURLSessionDataTask物件以及AFURLSessionManager物件相關聯
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
delegate.manager = self;
delegate.completionHandler = completionHandler;
dataTask.taskDescription = self.taskDescriptionForSessionTasks;
[self setDelegate:delegate forTask:dataTask];
delegate.uploadProgressBlock = uploadProgressBlock;
delegate.downloadProgressBlock = downloadProgressBlock;
}
複製程式碼
接著點[self setDelegate:delegate forTask:dataTask];
進去
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
forTask:(NSURLSessionTask *)task
{
// 在debug模式下如果缺少引數則會crash
NSParameterAssert(task);
NSParameterAssert(delegate);
// 加鎖保證對字典的操作執行緒安全
[self.lock lock];
// 將NSURLSessionTask物件的taskIdentifier作為key,將與之對應的AFURLSessionManagerTaskDelegate物件作為value,存放到字典中
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
// 為task新增開始與暫停通知
[self addNotificationObserverForTask:task];
[self.lock unlock];
}
複製程式碼
1.在[self addNotificationObserverForTask:task];
方法中新增對task相關通知的接收
- (void)addNotificationObserverForTask:(NSURLSessionTask *)task {
// 接收task開啟和暫停的通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidResume:) name:AFNSURLSessionTaskDidResumeNotification object:task];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidSuspend:) name:AFNSURLSessionTaskDidSuspendNotification object:task];
}
複製程式碼
在通知的響應方法中轉化為AFNetworking
對外暴漏的通知AFNetworkingTaskDidResumeNotification
和AFNetworkingTaskDidSuspendNotification
- (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];
});
}
}
}
複製程式碼
為什麼不直接傳送通知,而是先接收通知然後經過處理後再傳送通知呢?這是因為AFNetworking
把NSURLSessionTask
的suspend
和resume
方法利用runtime
的Method Swizzling
交換為af_suspend
和af_resume
,並在交換後的方法中傳送通知以實現對task
開始與暫停的監聽, 但是在一個專案中可能有的網路請求不是當前session
的,所以要通過task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks
進行判斷這個網路請求task是否來自當前session
,其中self.taskDescriptionForSessionTasks
其實就是AFURLSessionManager
物件的指標字串
- (NSString *)taskDescriptionForSessionTasks {
return [NSString stringWithFormat:@"%p", self];
}
複製程式碼
到此為止,對task的處理就結束了。
3.請求回撥
AFURLSessionManager
遵守了<NSURLSessionDelegate> <NSURLSessionTaskDelegate> <NSURLSessionDataDelegate> <NSURLSessionDownloadDelegate>
四個代理並實現了15個代理方法
AFURLSessionManagerTaskDelegate
只實現了其中6個
1.先看第一個代理- (void)URLSession:(__unused NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
的實現
- (void)URLSession:(__unused NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
// 因為self.manager屬性關鍵字是weak,所以為了防止被釋放就用__strong
__strong AFURLSessionManager *manager = self.manager;
// 用來儲存請求返回的資料,為了可以在block中進行修改,用了__block
__block id responseObject = nil;
// 用來儲存傳送通知時傳遞的資料,為了可以在block中進行修改,用了__block,並進行賦值
__block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;
// 使用臨時變數儲存請求到的資料,並把儲存資料的屬性清空,節約記憶體
//Performance Improvement from #2672
NSData *data = nil;
if (self.mutableData) {
data = [self.mutableData copy];
//We no longer need the reference, so nil it out to gain back some memory.
self.mutableData = nil;
}
// 如果設定了下載檔案的儲存路徑,就傳遞儲存路徑,否則如果有請求到的資料,就傳遞請求到的資料
if (self.downloadFileURL) {
userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
} else if (data) {
userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data;
}
// 如果請求出錯
if (error) {
// 傳遞錯誤資訊
userInfo[AFNetworkingTaskDidCompleteErrorKey] = error;
// 使用者可以自定義排程組和佇列並利用dispatch_group_notify實現對回撥完成的監控
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];
});
});
});
}
}
複製程式碼
2.第二個代理- (void)URLSession:(__unused NSURLSession *)session dataTask:(__unused NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
的實現相對簡單
- (void)URLSession:(__unused NSURLSession *)session
dataTask:(__unused NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data
{
// 更新下載進度物件的屬性
self.downloadProgress.totalUnitCount = dataTask.countOfBytesExpectedToReceive;
self.downloadProgress.completedUnitCount = dataTask.countOfBytesReceived;
// 儲存傳遞的資料
[self.mutableData appendData:data];
}
複製程式碼
3.第三個代理- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
是在向伺服器傳遞資料時呼叫
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didSendBodyData:(int64_t)bytesSent
totalBytesSent:(int64_t)totalBytesSent
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend{
// 更新上傳進度物件的屬性
self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend;
self.uploadProgress.completedUnitCount = task.countOfBytesSent;
}
複製程式碼
4.第四個代理是在執行下載任務時,定期呼叫的
- (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;
}
複製程式碼
5.第五個代理在重啟下載時呼叫
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes{
// 更新下載進度物件的屬性
self.downloadProgress.totalUnitCount = expectedTotalBytes;
self.downloadProgress.completedUnitCount = fileOffset;
}
複製程式碼
6.第六個代理- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
是下載任務完成後的回撥
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
// 如果使用者設定了儲存下載檔案的路徑,就將下載完的檔案從臨時路徑移動過去,移動完成後傳送通知
self.downloadFileURL = nil;
if (self.downloadTaskDidFinishDownloading) {
self.downloadFileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
if (self.downloadFileURL) {
NSError *fileManagerError = nil;
if (![[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManagerError]) {
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:fileManagerError.userInfo];
}
}
}
}
複製程式碼
4.總結
到此為止,一個GET請求就結束了,總結一下AFNetworking
做的事情
- 首先,
AFHTTPSessionManager
利用引數method
、URLString
和parameters
生成一個NSMutableURLRequest
物件request
- 然後,
AFURLSessionManager
利用生成的request
生成一個NSURLSessionDataTask
物件dataTask
- 接下來,把
dataTask
和AFURLSessionManagerTaskDelegate
的物件delegate
利用字典一一對應儲存,將delegate
的進度屬性的取消、暫停和開始和dataTask
相對的方法相關聯,並對delegate
和dataTask
的相關屬性新增觀察者對delegate
的上傳下載進度屬性賦值 - 並且,為
dataTask
的開始於停止新增通知監聽,並向外部傳送通知 - 接著,呼叫
dataTask
的resume
方法進行網路請求 - 最後,通過
AFURLSessionManagerTaskDelegate
的物件delegate
實現的六個代理方法監聽進度以及處理請求返回的資料,如果需要解析資料會在非同步併發佇列中進行,然後在最開始生成的最大併發數為1的NSOperationQueue
的物件operationQueue
序列回撥結果。
原始碼閱讀系列:AFNetworking
原始碼閱讀:AFNetworking(二)——AFURLRequestSerialization
原始碼閱讀:AFNetworking(三)——AFURLResponseSerialization
原始碼閱讀:AFNetworking(四)——AFSecurityPolicy
原始碼閱讀:AFNetworking(五)——AFNetworkReachabilityManager
原始碼閱讀:AFNetworking(六)——AFURLSessionManager
原始碼閱讀:AFNetworking(七)——AFHTTPSessionManager
原始碼閱讀:AFNetworking(八)——AFAutoPurgingImageCache
原始碼閱讀:AFNetworking(九)——AFImageDownloader
原始碼閱讀:AFNetworking(十)——AFNetworkActivityIndicatorManager
原始碼閱讀:AFNetworking(十一)——UIActivityIndicatorView+AFNetworking
原始碼閱讀:AFNetworking(十二)——UIButton+AFNetworking
原始碼閱讀:AFNetworking(十三)——UIImageView+AFNetworking
原始碼閱讀:AFNetworking(十四)——UIProgressView+AFNetworking