AFNetworking3.0源碼淺析

AFNetworking是iOS開(kāi)發(fā)中基本都會(huì)用到的一個(gè)網(wǎng)絡(luò)框架琐脏;閱讀其源碼對(duì)理解iOS的網(wǎng)絡(luò)開(kāi)發(fā)很有幫助量蕊。

概述

在3.0版本中叮称,AFNetworking也放棄了使用NSURLConnection漠畜,全面轉(zhuǎn)向NSURLSession,其框架結(jié)構(gòu)基本如下:

框架結(jié)構(gòu)圖

通常我們直接使用的是AFHTTPSessionManager類锭沟,因?yàn)樵趇OS開(kāi)發(fā)中基本都是采用http(https)類型的網(wǎng)絡(luò)通信協(xié)議笼痹。

AFURLRequestSerialization模塊分析

AFURLRequestSerialization主要作用是生成一個(gè)封裝好各種參數(shù)的NSMutableURLRequest配喳。其文件內(nèi)部的結(jié)構(gòu)如下:

AFURLRequestSerialization結(jié)構(gòu)圖

創(chuàng)建網(wǎng)絡(luò)請(qǐng)求時(shí),我們根據(jù)不同需要凳干,選擇AFHTTPRequestSerializer晴裹、AFJSONRequestSerializerAFPropertyRequestSerializer三種Serializer之一來(lái)創(chuàng)建Request救赐。

// 基本邏輯如下:
if (method == get || head || delete) {
    // 將parameters拼接成URL字符串加到URL后
} else if (multipart表單提交) {
    // 通過(guò)圖中的AFStreamingMultipartFormData來(lái)將參數(shù)及相應(yīng)數(shù)據(jù)變成一項(xiàng)項(xiàng)的AFHTTPBodyPart中涧团,然后給HTTPBodyStream賦值
} else { // put || post || patch
    
    /**
     1.HTTPSerializer: 將參數(shù)編碼拼接成字符串轉(zhuǎn)成NSData
     2.JSONSerializer: 將參數(shù)以dataWithJSONObject方式轉(zhuǎn)成NSData
     3.PropertyListSerializer:將參數(shù)以dataWithPropertyList方式轉(zhuǎn)成NSData
      然后將NSData放到HTTPBody中
    */
}
/* HTTPBody和HTTPBodyStream是互斥的 */

// 附multipart格式
let boundary = wfWiEWrgEFA9A78512weF7106A
--boundary //開(kāi)始
Content-Disposition: form-data; name="status" 

abcddsdf 
--boundary
Content-Disposition: form-data; name="source"

2582981980
--boundary
Content-Disposition: form-data; name="access_token"

2.00nVexdfsfsoBgbvnoCcdfs4esfac4c4Nksmwc
--boundary
Content-Disposition: form-data; name="pic"; filename="test.png";Content-Type=image/png

...這里是文件的二進(jìn)制數(shù)據(jù)... 
--boundary-- //結(jié)束

AFURLResponseSerialization模塊分析

Response的序列化相對(duì)比較簡(jiǎn)單,結(jié)構(gòu)圖如下:


image.png

代碼簡(jiǎn)析

- (BOOL)validateResponse:(NSHTTPURLResponse *)response
                    data:(NSData *)data
                   error:(NSError * __autoreleasing *)error
{
    BOOL responseIsValid = YES;
    NSError *validationError = nil;

    // 判斷是HTTPURLResponse類型
    if (response && [response isKindOfClass:[NSHTTPURLResponse class]]) {
        // 如果response中的MIMEType不是可接受的類型
        if (self.acceptableContentTypes &&
            ![self.acceptableContentTypes containsObject:[response MIMEType]] &&
            !([response MIMEType] == nil && [data length] == 0)) {
            if ([data length] > 0 && [response URL]) {
                NSMutableDictionary *mutableUserInfo = [@{
                                                          NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: unacceptable content-type: %@", @"AFNetworking", nil), [response MIMEType]],
                                                          NSURLErrorFailingURLErrorKey:[response URL],
                                                          AFNetworkingOperationFailingURLResponseErrorKey: response,
                                                        } mutableCopy];
                if (data) {
                    mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
                }

                validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:mutableUserInfo], validationError);
            }

            responseIsValid = NO;
        }
        
        // 如果status !(200-299)经磅,同樣創(chuàng)建錯(cuò)誤信息
        if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode] && [response URL]) {
            NSMutableDictionary *mutableUserInfo = [@{
                                               NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: %@ (%ld)", @"AFNetworking", nil), [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode], (long)response.statusCode],
                                               NSURLErrorFailingURLErrorKey:[response URL],
                                               AFNetworkingOperationFailingURLResponseErrorKey: response,
                                       } mutableCopy];

            if (data) {
                mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
            }

            validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError);

            responseIsValid = NO;
        }
    }

    if (error && !responseIsValid) {
        *error = validationError;
    }

    return responseIsValid;
}

AFSecurityPolicy模塊分析

有三種驗(yàn)證模式

typedef NS_ENUM(NSUInteger, AFSSLPinningMode) {
    AFSSLPinningModeNone,       // 會(huì)在系統(tǒng)信任的證書列表里驗(yàn)證服務(wù)器的證書是否可信
    AFSSLPinningModePublicKey,  // 需要客戶端保存有服務(wù)器端的證書拷貝泌绣,驗(yàn)證客戶端保存的與要驗(yàn)證的服務(wù)器的是否一致
    AFSSLPinningModeCertificate,// 需要客戶端保存有服務(wù)器端的證書拷貝,但是只驗(yàn)證公鑰是否一致
};

默認(rèn)采用的是AFSSLPinningModeNone预厌。

AFNetworkReachabilityManager模塊分析

網(wǎng)絡(luò)狀態(tài)

typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) {
    AFNetworkReachabilityStatusUnknown          = -1,
    AFNetworkReachabilityStatusNotReachable     = 0,
    AFNetworkReachabilityStatusReachableViaWWAN = 1,
    AFNetworkReachabilityStatusReachableViaWiFi = 2,
};

AFURLSessionManager/AFHTTPSessionManager模塊分析

同樣阿迈,結(jié)構(gòu)圖如下:

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

每個(gè)NSURLSessionTask對(duì)應(yīng)著一個(gè)AFURLSessionManagerTaskDelegate,在AFURLSessionManager以鍵值對(duì)的方式存儲(chǔ)在manager

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

    [self.lock lock];
    // 用taskIdentifier(每個(gè)session創(chuàng)建的task的identifier是唯一的)作為key值轧叽,存儲(chǔ)delegate
    self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
    [self addNotificationObserverForTask:task];
    [self.lock unlock];
}

- (AFURLSessionManagerTaskDelegate *)delegateForTask:(NSURLSessionTask *)task {
    NSParameterAssert(task);

    AFURLSessionManagerTaskDelegate *delegate = nil;
    [self.lock lock];
    delegate = self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)];
    [self.lock unlock];

    return delegate;
}

即每個(gè)task上的處理交由AFURLSessionManagerTaskDelegate處理苗沧。

其它

_AFURLSessionTaskSwizzling中采用method swizzling,交換了系統(tǒng)NSURLSessionTasksuspendresume方法炭晒,分別在兩個(gè)方法原有的基礎(chǔ)上添加了通知待逞。

參考:
chenxianming的博客

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市网严,隨后出現(xiàn)的幾起案子识樱,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,627評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件牺荠,死亡現(xiàn)場(chǎng)離奇詭異翁巍,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)休雌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門灶壶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人杈曲,你說(shuō)我怎么就攤上這事驰凛。” “怎么了担扑?”我有些...
    開(kāi)封第一講書人閱讀 169,346評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵恰响,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我涌献,道長(zhǎng)胚宦,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 60,097評(píng)論 1 300
  • 正文 為了忘掉前任燕垃,我火速辦了婚禮枢劝,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘卜壕。我一直安慰自己您旁,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,100評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布轴捎。 她就那樣靜靜地躺著鹤盒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪侦副。 梳的紋絲不亂的頭發(fā)上侦锯,一...
    開(kāi)封第一講書人閱讀 52,696評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音跃洛,去河邊找鬼率触。 笑死终议,一個(gè)胖子當(dāng)著我的面吹牛汇竭,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播穴张,決...
    沈念sama閱讀 41,165評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼细燎,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了皂甘?” 一聲冷哼從身側(cè)響起玻驻,我...
    開(kāi)封第一講書人閱讀 40,108評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后璧瞬,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體户辫,經(jīng)...
    沈念sama閱讀 46,646評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,709評(píng)論 3 342
  • 正文 我和宋清朗相戀三年嗤锉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了渔欢。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,861評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡瘟忱,死狀恐怖奥额,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情访诱,我是刑警寧澤垫挨,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站触菜,受9級(jí)特大地震影響九榔,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜涡相,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,196評(píng)論 3 336
  • 文/蒙蒙 一帚屉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧漾峡,春花似錦攻旦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,698評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至槽袄,卻和暖如春烙无,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背遍尺。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,804評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工截酷, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人乾戏。 一個(gè)月前我還...
    沈念sama閱讀 49,287評(píng)論 3 379
  • 正文 我出身青樓迂苛,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親鼓择。 傳聞我的和親對(duì)象是個(gè)殘疾皇子三幻,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,860評(píng)論 2 361