AFNetworking3 源碼分析

聲明:以下是對所看源碼和相關(guān)資料的整理

框架組成

AFNetworking作為開發(fā)中使用最多的第三方網(wǎng)絡(luò)庫吓著,功能完善,代碼結(jié)構(gòu)簡潔发侵、清晰易懂氧腰,非常值得仔細(xì)研究枫浙。先看一下結(jié)構(gòu)目錄

目錄結(jié)構(gòu)圖

除去Support Files和AFNetworking.h刨肃,分為如下5個功能模塊:

  • 網(wǎng)絡(luò)通信模塊(NSURLSession)
  • 網(wǎng)絡(luò)狀態(tài)監(jiān)聽模塊(Reachability)
  • 網(wǎng)絡(luò)通信安全策略模塊(Security)
  • 網(wǎng)絡(luò)通信信息序列化/反序列化模塊(Serialization)
  • 對于iOS UIKit庫的擴(kuò)展(UIKit)

AFURLSessionManager是AFNetworking的核心類,AFHTTPSessionManager繼承于AFURLSessionManager箩帚, 針對HTTP協(xié)議傳輸做了特化真友。AFURLResponseSerialization, AFSecurityPolicy, AFNetworkReachabilityManager作為AFURLSessionManager的屬性紧帕,提供相應(yīng)的功能盔然。

還有一個單獨(dú)的UIKit包提供了對iOS UIKit類庫的擴(kuò)展與工具類。

網(wǎng)絡(luò)通信核心AFURLSessionManager

AFNetworking是基于NSURLSession實(shí)現(xiàn)的是嗜。NSURLSession的使用方式如下:

  1. 創(chuàng)建NSURLSessionConfig對象
  2. 用之前創(chuàng)建的NSURLSessionConfig對象創(chuàng)建配置NSURLSession對象愈案。
  3. 用NSURLSession對象創(chuàng)建對應(yīng)的task對象,并用resume方法執(zhí)行之鹅搪。
  4. 用delegate方法或completion block 響應(yīng)網(wǎng)絡(luò)事件及數(shù)據(jù)站绪。

對應(yīng)于每次網(wǎng)絡(luò)會話,對應(yīng)一個NSURLSession對象丽柿,而每個會話恢准,可以生成若干task對象用于數(shù)據(jù)的交互。

在AFNetworking中甫题, AFURLSessionManager封裝并提供了上述網(wǎng)絡(luò)交互功能馁筐。

AFURLSessionManager.h的屬性

  • AFURLSessionManager所管理的Session對象

@property (readonly, nonatomic, strong) NSURLSession *session;

  • delegate所返回的NSOperationQueue

@property (readonly, nonatomic, strong) NSOperationQueue *operationQueue;

  • 解析網(wǎng)絡(luò)返回數(shù)據(jù)的對象(遵循AFURLResponseSerialization協(xié)議)

@property (nonatomic, strong) id <AFURLResponseSerialization> responseSerializer;

  • 用于處理網(wǎng)絡(luò)連接安全處理策略的AFSecurityPolicy對象

@property (nonatomic, strong) AFSecurityPolicy *securityPolicy;

  • 用于檢測網(wǎng)絡(luò)狀態(tài)的AFNetworkReachabilityManager對象

@property (readwrite, nonatomic, strong) AFNetworkReachabilityManager *reachabilityManager;

  • 當(dāng)前的Session對象的tasks

@property (readonly, nonatomic, strong) NSArray <NSURLSessionTask *> *tasks;

  • 當(dāng)前的Session對象的dataTasks

@property (readonly, nonatomic, strong) NSArray <NSURLSessionDataTask *> *dataTasks;

  • 當(dāng)前的Session對象的uploadTasks

@property (readonly, nonatomic, strong) NSArray <NSURLSessionUploadTask *> *uploadTasks;

  • 當(dāng)前的Session對象的downloadTasks

@property (readonly, nonatomic, strong) NSArray <NSURLSessionDownloadTask *> *downloadTasks;

  • 設(shè)置Call back隊(duì)列,默認(rèn)為main block

@property (nonatomic, strong, nullable) dispatch_queue_t completionQueue;

@property (nonatomic, strong, nullable) dispatch_group_t completionGroup;

結(jié)合上面所說再看這些屬性的功能都很清楚了坠非。

AFURLSessionManager.m

.m代碼中還有AFURLSessionManagerTaskDelegate敏沉、_AFURLSessionTaskSwizzling兩個類,功能如下

  • AFURLSessionManagerTaskDelegate管理進(jìn)度
  • _AFURLSessionTaskSwizzling 調(diào)劑方法

在使用 AFURLSessionManager 時炎码,第一件要做的事情一定是初始化:

- (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;

    //為已有的 task 設(shè)置代理
    [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;
}

其中mutableTaskDelegatesKeyedByTaskIdentifier是存儲task與AFURLSessionManagerTaskDelegate的詞典盟迟,在AFNetworking中,每一個task都會被匹配一個AFURLSessionManagerTaskDelegate來做task的delegate的事件處理

self.lock用于確保mutableTaskDelegatesKeyedByTaskIdentifier在多線程訪問時的線程安全

管理 NSURLSessionTask

在初始化AFURLSessionManager之后辅肾,我們可以通過以下方法創(chuàng)建 NSURLSessionDataTask 的實(shí)例:

- (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;

這些接口的實(shí)現(xiàn)都類似队萤,以- [AFURLSessionManager dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:] 方法為例轮锥,分析它是如何實(shí)例化并返回一個 NSURLSessionTask 的實(shí)例的:

- (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;
}

上述方法完成了兩件事情:

  1. 生成一個data task對象矫钓,并返回。這里為了避免iOS 8.0以下版本中偶發(fā)的taskIdentifiers不唯一的bug舍杜,調(diào)用了url_session_manager_create_task_safely函數(shù)新娜,可能是因?yàn)閕OS 8.0以下版本中會并發(fā)地創(chuàng)建多個task對象,而同步有沒有做好既绩,導(dǎo)致taskIdentifiers不唯一…
  2. 為該data task對象生成一個匹配的AFURLSessionManagerTaskDelegate對象并關(guān)聯(lián)概龄,這里說明以下[addDelegateForDataTask:uploadProgress: downloadProgress: completionHandler:]方法。
- (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] init];
    delegate.manager = self;
    delegate.completionHandler = completionHandler;

    dataTask.taskDescription = self.taskDescriptionForSessionTasks;
    [self setDelegate:delegate forTask:dataTask];

    delegate.uploadProgressBlock = uploadProgressBlock;
    delegate.downloadProgressBlock = downloadProgressBlock;
}

在這個方法中同時調(diào)用了另一個方法 - [AFURLSessionManager setDelegate:forTask:] 來設(shè)置代理

- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
            forTask:(NSURLSessionTask *)task
{
    NSParameterAssert(task);
    NSParameterAssert(delegate);

    [self.lock lock];
    self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
    [delegate setupProgressForTask:task];
    [self addNotificationObserverForTask:task];
    [self.lock unlock];
}

該方法使用 NSLock 來保證不同線程使用 mutableTaskDelegatesKeyedByTaskIdentifier 時饲握,不會出現(xiàn) 線程競爭的問題私杜。

看了這兩個方法蚕键,AFURLSessionManager通過字典 mutableTaskDelegatesKeyedByTaskIdentifier 來存儲并管理每一個 NSURLSessionTask ,它以 taskIdentifier 為鍵存儲 task衰粹。NSURLSessionTask的Delegate返回結(jié)果都在AFURLSessionManagerTaskDelegate中處理锣光,再由AFURLSessionManagerTaskDelegate傳給AFURLSessionManager。

實(shí)現(xiàn) NSURLSessionDelegate 等協(xié)議中的代理方法

在 AFURLSessionManager 的頭文件中可以看到铝耻,它遵循了多個協(xié)議誊爹,其中包括:

  • NSURLSessionDelegate
  • NSURLSessionTaskDelegate
  • NSURLSessionDataDelegate
  • NSURLSessionDownloadDelegate

它在初始化方法 - [AFURLSessionManager initWithSessionConfiguration:] 將NSURLSession的代理指向 self,然后實(shí)現(xiàn)這些方法瓢捉,提供更簡潔的block的接口:

- (void)setSessionDidBecomeInvalidBlock:(nullable void (^)(NSURLSession *session, NSError *error))block;
- (void)setSessionDidReceiveAuthenticationChallengeBlock:(nullable NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * _Nullable __autoreleasing * _Nullable credential))block;
- (void)setTaskNeedNewBodyStreamBlock:(nullable NSInputStream * (^)(NSURLSession *session, NSURLSessionTask *task))block;
...

它為所有的代理協(xié)議都提供了對應(yīng)的block接口频丘,方法實(shí)現(xiàn)的思路都是相似的,我們以 - [AFNRLSessionManager setSessionDidBecomeInvalidBlock:]為例泡态。

首先調(diào)用 setter 方法搂漠,將 block 存入 sessionDidBecomeInvalid 屬性中:

- (void)setSessionDidBecomeInvalidBlock:(void (^)(NSURLSession *session, NSError *error))block {
    self.sessionDidBecomeInvalid = block;
}

當(dāng)代理方法調(diào)用時,如果存在對應(yīng)的 block某弦,會執(zhí)行對應(yīng)的 block:

- (void)URLSession:(NSURLSession *)session
didBecomeInvalidWithError:(NSError *)error  
{
    if (self.sessionDidBecomeInvalid) {
        self.sessionDidBecomeInvalid(session, error);
    }

    [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDidInvalidateNotification object:session];
}

其他相似的接口實(shí)現(xiàn)也都差不多状答,直接跳過。

使用 AFURLSessionManagerTaskDelegate 管理進(jìn)度

AFURLSessionManagerTaskDelegate主要為task提供進(jìn)度管理功能刀崖,并在task結(jié)束時回調(diào)惊科,也就是調(diào)用在- [AFURLSessionManager dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:]等方法中傳入的completionHandler 。

AFURLSessionManagerTaskDelegate 對進(jìn)度進(jìn)行跟蹤亮钦,以uploadProgress 為例:

- (void)setupProgressForTask:(NSURLSessionTask *)task {
    __weak __typeof__(task) weakTask = task;

    self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend;
    self.downloadProgress.totalUnitCount = task.countOfBytesExpectedToReceive;
    [self.uploadProgress setCancellable:YES];
    [self.uploadProgress setCancellationHandler:^{
        __typeof__(weakTask) strongTask = weakTask;
        [strongTask cancel];
    }];
    [self.uploadProgress setPausable:YES];
    [self.uploadProgress setPausingHandler:^{
        __typeof__(weakTask) strongTask = weakTask;
        [strongTask suspend];
    }];
    if ([self.uploadProgress respondsToSelector:@selector(setResumingHandler:)]) {
        [self.uploadProgress setResumingHandler:^{
            __typeof__(weakTask) strongTask = weakTask;
            [strongTask resume];
        }];
    }

    [self.downloadProgress setCancellable:YES];
    [self.downloadProgress setCancellationHandler:^{
        __typeof__(weakTask) strongTask = weakTask;
        [strongTask cancel];
    }];
    [self.downloadProgress setPausable:YES];
    [self.downloadProgress setPausingHandler:^{
        __typeof__(weakTask) strongTask = weakTask;
        [strongTask suspend];
    }];

    if ([self.downloadProgress respondsToSelector:@selector(setResumingHandler:)]) {
        [self.downloadProgress setResumingHandler:^{
            __typeof__(weakTask) strongTask = weakTask;
            [strongTask resume];
        }];
    }

    [task addObserver:self
           forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))
              options:NSKeyValueObservingOptionNew
              context:NULL];
    [task addObserver:self
           forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))
              options:NSKeyValueObservingOptionNew
              context:NULL];

    [task addObserver:self
           forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))
              options:NSKeyValueObservingOptionNew
              context:NULL];
    [task addObserver:self
           forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToSend))
              options:NSKeyValueObservingOptionNew
              context:NULL];

    [self.downloadProgress addObserver:self
                            forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
                               options:NSKeyValueObservingOptionNew
                               context:NULL];
    [self.uploadProgress addObserver:self
                          forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
                             options:NSKeyValueObservingOptionNew
                             context:NULL];
}

前半部分對應(yīng) NSProgress 的狀態(tài)改變時馆截,調(diào)用 resume suspend 等方法改變 task 的狀態(tài)。后半部分對task 和 NSProgress 屬性進(jìn)行鍵值觀測蜂莉。

在 observeValueForKeypath:ofObject:change:context: 方法中改變進(jìn)度蜡娶,并調(diào)用 block

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
    if ([object isKindOfClass:[NSURLSessionTask class]] || [object isKindOfClass:[NSURLSessionDownloadTask class]]) {
        if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesReceived))]) {
            self.downloadProgress.completedUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
        } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))]) {
            self.downloadProgress.totalUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
        } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesSent))]) {
            self.uploadProgress.completedUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
        } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToSend))]) {
            self.uploadProgress.totalUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
        }
    }
    else if ([object isEqual:self.downloadProgress]) {
        if (self.downloadProgressBlock) {
            self.downloadProgressBlock(object);
        }
    }
    else if ([object isEqual:self.uploadProgress]) {
        if (self.uploadProgressBlock) {
            self.uploadProgressBlock(object);
        }
    }
}

對象的某些屬性改變時更新 NSProgress 對象或使用 block 傳遞 NSProgress 對象 self.uploadProgressBlock(object)

代理方法 URLSession:task:didCompleteWithError:

在每一個NSURLSessionTask結(jié)束時,都會在代理方法 URLSession:task:didCompleteWithError:中:

  1. 調(diào)用傳入的completionHander
  2. 發(fā)出AFNetworkingTaskDidCompleteNotification通知
- (void)URLSession:(__unused NSURLSession *)session
              task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
    __strong AFURLSessionManager *manager = self.manager;

    __block id responseObject = nil;

    __block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
    
   //從 mutableData 中取出了數(shù)據(jù)映穗,設(shè)置了 userInfo
    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;
        
        //如果當(dāng)前 manager 持有 completionGroup 或者 completionQueue 就使用它們窖张。否則會創(chuàng)建一個 dispatch_group_t 并在主線程中調(diào)用 completionHandler 并發(fā)送通知(在主線程中)。
        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 {
        //如果在執(zhí)行當(dāng)前 task 時沒有遇到錯誤蚁滋,那么先 對數(shù)據(jù)進(jìn)行序列化 宿接,然后同樣調(diào)用 block 并發(fā)送通知。
        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];
                });
            });
        });
    }
#pragma clang diagnostic pop
}

代理方法 URLSession:dataTask:didReceiveData: 和 - URLSession:downloadTask:didFinishDownloadingToURL:

這兩個代理方法分別會在收到數(shù)據(jù)或者完成下載對應(yīng)文件時調(diào)用辕录,作用分別是為 mutableData 追加數(shù)據(jù)和處理下載的文件:

_AFURLSessionTaskSwizzling 調(diào)劑方法

_AFURLSessionTaskSwizzling 的唯一功能就是修改 NSURLSessionTask 的 resume 和 suspend 方法睦霎,使用下面的方法替換原有的實(shí)現(xiàn)

- (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];
    }
}

這樣做的目的是為了在方法 resume 或者 suspend 被調(diào)用時發(fā)出通知。

具體方法調(diào)劑的過程是在 + load 方法中進(jìn)行的

+ (void)load {
        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];
    }
}
  1. 首先用 NSClassFromString(@"NSURLSessionTask") 判斷當(dāng)前部署的 iOS 版本是否含有類 NSURLSessionTask
    2.因?yàn)?iOS7 和 iOS8 上對于 NSURLSessionTask 的實(shí)現(xiàn)不同走诞,所以會通過 - [NSURLSession dataTaskWithURL:] 方法返回一個 NSURLSessionTask 實(shí)例
  2. 取得當(dāng)前類 _AFURLSessionTaskSwizzling 中的實(shí)現(xiàn) af_resume
  3. 判斷當(dāng)前類 currentClass 有 resume 方法
  4. 使用swizzleResumeAndSuspendMethodForClass: 調(diào)劑該類的 resume 和 suspend 方法
  5. currentClass = [currentClass superclass]

這里復(fù)雜的實(shí)現(xiàn)是為了解決 bug #2702

引入 AFSecurityPolicy 保證請求的安全

AFSecurityPolicy 是 AFNetworking 用來保證 HTTP 請求安全的類副女,它被 AFURLSessionManager 持有,如果你在 AFURLSessionManager 的實(shí)現(xiàn)文件中搜索 self.securityPolicy 蚣旱,你只會得到三條結(jié)果:

  1. 初始化 self.securityPolicy = [AFSecurityPolicy defaultPolicy]
  2. 收到連接層的驗(yàn)證請求時
  3. 任務(wù)接收到驗(yàn)證請求時

在 API 調(diào)用上碑幅,后兩者都調(diào)用了 - [AFSecurityPolicy evaluateServerTrust:forDomain:] 方法來判斷 當(dāng)前服務(wù)器是否被信任 戴陡,我們會在接下來的文章中具體介紹這個方法的實(shí)現(xiàn)的作用。

- (void)URLSession:(NSURLSession *)session
              task:(NSURLSessionTask *)task
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge  
 completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
    NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
    __block NSURLCredential *credential = nil;

    if (self.taskDidReceiveAuthenticationChallenge) {
        disposition = self.taskDidReceiveAuthenticationChallenge(session, task, challenge, &credential);
    } else {
        if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
            if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
                disposition = NSURLSessionAuthChallengeUseCredential;
                credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
            } else {
                disposition = NSURLSessionAuthChallengeRejectProtectionSpace;
            }
        } else {
            disposition = NSURLSessionAuthChallengePerformDefaultHandling;
        }
    }

    if (completionHandler) {
        completionHandler(disposition, credential);
    }
}

如果沒有傳入 taskDidReceiveAuthenticationChallenge block沟涨,只有在上述方法返回 YES 時猜欺,才會獲得認(rèn)證憑證 credential 。

引入 AFNetworkReachabilityManager 監(jiān)控網(wǎng)絡(luò)狀態(tài)

與 AFSecurityPolicy 相同拷窜, AFURLSessionManager 對網(wǎng)絡(luò)狀態(tài)的監(jiān)控是由 AFNetworkReachabilityManager 來負(fù)責(zé)的开皿,它僅僅是持有一個 AFNetworkReachabilityManager 的對象。

真正需要判斷網(wǎng)絡(luò)狀態(tài)時篮昧,仍然需要開發(fā)者調(diào)用對應(yīng)的API獲取網(wǎng)絡(luò)狀態(tài)赋荆。

小結(jié)

  1. AFURLSessionManager是對NSURLSession的封裝
  2. 它通過 - [AFURLSessionManager dataTaskWithRequest:completionHandler:]等接口創(chuàng)建 NSURLSessionDataTask的實(shí)例
  3. 持有一個字典mutableTaskDelegatesKeyedByTaskIdentifier 管理這些dataTask實(shí)例
  4. 引入AFURLSessionManagerTaskDelegate來對傳入的 uploadProgressBlock和downloadProgressBlock的completionHandler在合適的時間進(jìn)行調(diào)用
  5. 實(shí)現(xiàn)了全部的代理方法來提供block接口
  6. 通過方法調(diào)劑在dataTask狀態(tài)改變時,發(fā)出通知
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末懊昨,一起剝皮案震驚了整個濱河市窄潭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌酵颁,老刑警劉巖嫉你,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異躏惋,居然都是意外死亡幽污,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進(jìn)店門簿姨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來距误,“玉大人,你說我怎么就攤上這事扁位∽继叮” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵域仇,是天一觀的道長刑然。 經(jīng)常有香客問我,道長暇务,這世上最難降的妖魔是什么泼掠? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮般卑,結(jié)果婚禮上武鲁,老公的妹妹穿的比我還像新娘爽雄。我一直安慰自己蝠检,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布挚瘟。 她就那樣靜靜地躺著叹谁,像睡著了一般饲梭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上焰檩,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天憔涉,我揣著相機(jī)與錄音,去河邊找鬼析苫。 笑死兜叨,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的衩侥。 我是一名探鬼主播国旷,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼茫死!你這毒婦竟也來了跪但?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤峦萎,失蹤者是張志新(化名)和其女友劉穎屡久,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體爱榔,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡被环,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了详幽。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蛤售。...
    茶點(diǎn)故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖妒潭,靈堂內(nèi)的尸體忽然破棺而出悴能,到底是詐尸還是另有隱情,我是刑警寧澤雳灾,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布漠酿,位于F島的核電站,受9級特大地震影響谎亩,放射性物質(zhì)發(fā)生泄漏炒嘲。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一匈庭、第九天 我趴在偏房一處隱蔽的房頂上張望夫凸。 院中可真熱鬧,春花似錦阱持、人聲如沸夭拌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鸽扁。三九已至蒜绽,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間桶现,已是汗流浹背躲雅。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留骡和,地道東北人相赁。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像慰于,于是被迫代替她去往敵國和親噪生。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容