AFNetworking 源碼閱讀筆記(二)

AFNetworking

AFNetworking 是 Objective-C 中用于網(wǎng)絡(luò)請(qǐng)求的第三方框架,我們一般使用它來(lái)封裝網(wǎng)絡(luò)請(qǐng)求,這篇文章記錄了閱讀 AFNetworking(Version 3.1.0) 源碼的筆記榜聂,簡(jiǎn)單的研究了它的實(shí)現(xiàn)細(xì)節(jié)闹司。

AFHTTPSessionManager

AFHTTPSessionManager 繼承自 AFURLSessionManager嗤朴,主要是提供了一系列對(duì)外暴露的屬性和方法配椭,如下圖:

可以看出對(duì)外暴露的有:

三個(gè)屬性:baseURLrequestSerializer雹姊、responseSerializer

三個(gè)初始化方法:

+ (instancetype)manager
- (instancetype)initWithBaseURL:(nullable NSURL *)url
- (instancetype)initWithBaseURL:(nullable NSURL *)url
           sessionConfiguration:(nullable NSURLSessionConfiguration *)configuration

還有一系列請(qǐng)求方法:GET股缸、HEADPOST吱雏、PUT敦姻、PATCHDELETE歧杏。

初始化與請(qǐng)求方法

其中所有的初始化方法镰惦,最后都到initWithBaseURL:方法里來(lái)初始化對(duì)象,需要注意的是默認(rèn) response 是 JSON 類型的犬绒。

- (instancetype)initWithBaseURL:(NSURL *)url
           sessionConfiguration:(NSURLSessionConfiguration *)configuration
{
 ...
    self.baseURL = url;
 // 默認(rèn)的 request 和 response
    self.requestSerializer = [AFHTTPRequestSerializer serializer];
    self.responseSerializer = [AFJSONResponseSerializer serializer];

    return self;
}

而所有的請(qǐng)求方法旺入,GETHEAD凯力、POST茵瘾、PUTPATCH沮协、DELETE 等最終都是調(diào)用的 dataTaskWithHTTPMethod: 方法創(chuàng)建 task龄捡,區(qū)別是傳遞的method名稱不同卓嫂,方法體如下:

 NSError *serializationError = nil;
    // 創(chuàng)建 NSMutableURLRequest
    NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
    
    ...處理構(gòu)建 request 產(chǎn)生的錯(cuò)誤

    // 創(chuàng)建 dataTask
    __block NSURLSessionDataTask *dataTask = nil;
    dataTask = [self dataTaskWithRequest:request
                          uploadProgress:uploadProgress
                        downloadProgress:downloadProgress
                       completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
 ...
    }];

    return dataTask;

其中先創(chuàng)建 request慷暂,再根據(jù) request 創(chuàng)建 dataTask。

NSSecureCoding

@interface AFHTTPSessionManager : AFURLSessionManager <NSSecureCoding, NSCopying>

可以看出 AFHTTPSessionManager 實(shí)現(xiàn)了 NSSecureCoding 和 NSCoding 協(xié)議晨雳,其中 NSSecureCoding 是基于 NSCoding 協(xié)議行瑞。

NSSecureCoding 是蘋果在 iOS6 中引入的一個(gè)新協(xié)議,基于NSCoding餐禁。簡(jiǎn)單的說(shuō)就是在數(shù)據(jù)歸檔過(guò)程加了數(shù)據(jù)類型檢驗(yàn)血久,相對(duì)更安全一點(diǎn),使用起來(lái)需要多實(shí)現(xiàn)一個(gè)方法:

@protocol NSSecureCoding <NSCoding>
@required
+ (BOOL)supportsSecureCoding;
@end

小結(jié)

總的來(lái)說(shuō) AFHTTPSessionManager 只是在 AFURLSessionManager 的基礎(chǔ)上做了一層包裝帮非。

AFURLSessionManager

AFURLSessionManager 可以說(shuō)是 AFNetworking 的核心類氧吐,相應(yīng)的其內(nèi)部實(shí)現(xiàn)也復(fù)雜許多,先來(lái)看一張圖:

AFURLSessionManager.m 內(nèi)部除了 AFURLSessionManager 的實(shí)現(xiàn)外末盔,還包含兩個(gè)自定義類的實(shí)現(xiàn)筑舅,AFURLSessionManagerTaskDelegate 主要為 task 提供進(jìn)度管理功能,_AFURLSessionTaskSwizzling 則是修改 NSURLSessionTask 的 resumesuspend 方法陨舱,這里還是先從 AFURLSessionManager 初始化開始翠拣。

初始化

AFURLSessionManager 的初始化在initWithSessionConfiguration:方法里進(jìn)行了一系列的配置:

 // 默認(rèn)為 defaultSessionConfiguration
    if (!configuration) {
        configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    }
    self.sessionConfiguration = configuration;
    // 初始化代理隊(duì)列
    self.operationQueue = [[NSOperationQueue alloc] init];
    self.operationQueue.maxConcurrentOperationCount = 1;
    // 初始化 session
    self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
    // 初始化 response 序列,默認(rèn)為 JSON
    self.responseSerializer = [AFJSONResponseSerializer serializer];
    
    // AFSecurityPolicy 是 AFNetworking 用來(lái)保證 HTTP 請(qǐng)求安全的類游盲,它被 AFURLSessionManager 持有
    self.securityPolicy = [AFSecurityPolicy defaultPolicy];

#if !TARGET_OS_WATCH
    self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
#endif
    // 初始化用來(lái)存放 AFURLSessionManagerTaskDelegate 的字典
    self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
    // 初始化 NSLock
    self.lock = [[NSLock alloc] init];
    self.lock.name = AFURLSessionManagerLockName;
    
    ...為已有的 task 設(shè)置代理

創(chuàng)建 NSURLSessionTask

創(chuàng)建 NSURLSessionTask 的方法有以下三種:

dataTaskWithRequest:

uploadTaskWithRequest:

downloadTaskWithRequest:

這里以 dataTaskWithRequest: 方法為例误墓,可以看出其是用 NSURLSession 的 dataTaskWithRequest:方法創(chuàng)建出 dataTask蛮粮。

 __block NSURLSessionDataTask *dataTask = nil;
    // 解決iOS8之前的一個(gè)bug
    url_session_manager_create_task_safely(^{
        //  用NSURLSession創(chuàng)建NSURLSessionDataTask
        dataTask = [self.session dataTaskWithRequest:request];
    });
    
    // 給data task添加了一個(gè)AFURLSessionManagerTaskDelegate
    // 主要為 task 提供進(jìn)度管理功能
    [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];

    return dataTask;

需要注意的是在創(chuàng)建完 dataTask 之后,又通過(guò) addDelegateForDataTask:方法給 dataTask 添加了一個(gè) AFURLSessionManagerTaskDelegate谜慌,這個(gè) delegate 主要為 dataTask 提供上傳下載的進(jìn)度管理功能然想。

NSURLSession 相關(guān)的代理方法

@interface AFURLSessionManager : NSObject <NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate, NSSecureCoding, NSCopying>

AFURLSessionManager 遵循并實(shí)現(xiàn)了一系列 NSURLSession 相關(guān)的代理方法,不過(guò) AFURLSessionManager 為所有的代理協(xié)議都提供了對(duì)應(yīng)的 block 接口欣范,這樣不讓用戶自己實(shí)現(xiàn)相關(guān)方法又沾,對(duì)外只暴露出 block。這里舉個(gè)??:

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

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

可以看出在以上方法中熙卡,用戶只需要調(diào)用這個(gè) block杖刷, 就可以用來(lái)處理session無(wú)效的情況,這么做隱藏了細(xì)節(jié)驳癌,簡(jiǎn)化了調(diào)用滑燃。

AFURLSessionManagerTaskDelegate

AFURLSessionManagerTaskDelegate 繼承自 NSObject,主要作用是為 dataTask 提供進(jìn)度管理功能颓鲜。

需要注意的是在 AFURLSessionManagerTaskDelegate 的 setupProgressForTask方法中將 NSProgress 和 NSURLSessionTask 進(jìn)行了狀態(tài)綁定表窘,代碼如下:

    /*
     上傳的totalUnitCount就對(duì)應(yīng)期望發(fā)送(send)的數(shù)據(jù)大小,下載任務(wù)的就對(duì)應(yīng)期望接收(receive)的數(shù)據(jù)大小甜滨。
     
     接著就是設(shè)置這兩個(gè)NSProgress對(duì)應(yīng)的cancel乐严、pause和resume這三個(gè)狀態(tài),正好對(duì)應(yīng)session task的cancel衣摩、suspend和resume三個(gè)狀態(tài)
    */
    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];
        }];
    }
    
    // 給兩個(gè)progress添加KVO NSProgress的fractionCompleted屬性(任務(wù)已經(jīng)完成的比例昂验,取值為0~1)
    [self.downloadProgress addObserver:self
                            forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
                               options:NSKeyValueObservingOptionNew
                               context:NULL];
    [self.uploadProgress addObserver:self
                          forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
                             options:NSKeyValueObservingOptionNew
                             context:NULL];

_AFURLSessionTaskSwizzling

_AFURLSessionTaskSwizzling 的唯一功能就是修改 NSURLSessionTask 的 resumesuspend 方法,使用 af_resume艾扮、af_suspend 替換原有的實(shí)現(xiàn)既琴,主要實(shí)現(xiàn)是在 +load 方法中實(shí)現(xiàn)了 method swizzling。

總結(jié)

本文只列舉了一些主要的知識(shí)點(diǎn)泡嘴,詳細(xì)的可以看源碼注釋甫恩。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市酌予,隨后出現(xiàn)的幾起案子磺箕,更是在濱河造成了極大的恐慌,老刑警劉巖抛虫,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件松靡,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡莱褒,警方通過(guò)查閱死者的電腦和手機(jī)击困,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人阅茶,你說(shuō)我怎么就攤上這事蛛枚。” “怎么了脸哀?”我有些...
    開封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵蹦浦,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我撞蜂,道長(zhǎng)盲镶,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任蝌诡,我火速辦了婚禮溉贿,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘浦旱。我一直安慰自己宇色,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開白布颁湖。 她就那樣靜靜地躺著宣蠕,像睡著了一般。 火紅的嫁衣襯著肌膚如雪甥捺。 梳的紋絲不亂的頭發(fā)上抢蚀,一...
    開封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音镰禾,去河邊找鬼皿曲。 笑死,一個(gè)胖子當(dāng)著我的面吹牛羡微,可吹牛的內(nèi)容都是我干的谷饿。 我是一名探鬼主播惶我,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼妈倔,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了绸贡?” 一聲冷哼從身側(cè)響起盯蝴,我...
    開封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎听怕,沒(méi)想到半個(gè)月后捧挺,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡尿瞭,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年闽烙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡黑竞,死狀恐怖捕发,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情很魂,我是刑警寧澤扎酷,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站遏匆,受9級(jí)特大地震影響法挨,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜幅聘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一凡纳、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧帝蒿,春花似錦惫企、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至巩掺,卻和暖如春偏序,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背胖替。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工研儒, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人独令。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓端朵,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親燃箭。 傳聞我的和親對(duì)象是個(gè)殘疾皇子冲呢,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

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