源碼解析之--YTKNetwork網(wǎng)絡(luò)層

轉(zhuǎn)載鏈接:http://www.reibang.com/p/521a6437a0b6
參考上面鏈接才稍微看懂源碼的蚕苇,同時將原來的老版代碼,換成新版的凿叠;

使用后涩笤,畫出他們的調(diào)用關(guān)系,這樣方便看懂盒件,方便理解

還有其他好多理解文章蹬碧;

猿題庫 iOS 客戶端 網(wǎng)絡(luò)庫封裝
https://github.com/yuantiku/YTKNetwork

YTKNetwork 源碼分析
https://github.com/subvin/YTKNetworkAnalysis

BANetworking
https://github.com/beyondabel/BANetworking

首先

關(guān)于網(wǎng)絡(luò)層最先可能想到的是AFNetworking,或者Swift中的Alamofire炒刁,直接使用起來也特別的簡單恩沽,但是稍復(fù)雜的項目如果直接使用就顯得不夠用了,首先第三方耦合不說翔始,就光散落在各處的請求回調(diào)就難以后期維護(hù)罗心,所以一般會有針對性的再次封裝,往往初期可能業(yè)務(wù)相對簡單城瞎,考慮的方面較少渤闷,后期業(yè)務(wù)增加可能需要對網(wǎng)絡(luò)層進(jìn)行重構(gòu),一個好的架構(gòu)也一定是和業(yè)務(wù)層面緊密相連的脖镀,隨業(yè)務(wù)的增長不斷健壯的飒箭。
  最近也是看了YTKNetwork的源碼和相關(guān)博客,站在前輩的肩膀上寫下一些自己關(guān)于網(wǎng)絡(luò)層的解讀。

與業(yè)務(wù)層對接方式

常見的與業(yè)務(wù)層對接方式兩種:

  • 集約型:
      最典型就屬于上面說的AFNetworking弦蹂、Alamofire肩碟,發(fā)起網(wǎng)絡(luò)請求都集中在一個類上,請求回調(diào)通過Block凸椿、閉包實現(xiàn)的削祈,Block、閉包回調(diào)有比較好的靈活性削饵,可以方便的在任何位置發(fā)起請求岩瘦,同時也可能是不好的地方未巫,網(wǎng)絡(luò)請求回調(diào)散落在各處窿撬,不便于維護(hù)
      下面是一個集約型的網(wǎng)絡(luò)請求叙凡,大家使用集約型網(wǎng)絡(luò)請求有沒有遇到這么一個場景逸吵,請求回調(diào)后需要做比較多的處理窿侈,代碼量多的時候,會再定義一個私有方法把代碼寫在里面,在Block中調(diào)用在私有方法搁料。其實這樣處理本質(zhì)上和通過代理回調(diào)本質(zhì)上是一樣的。
[_manager GET:url parameters:param success:^(AFHTTPRequestOperation *operation, id responseObject) {
     //The data processing, Rendering interface
 } failure:^(AFHTTPRequestOperation *operation, NSError *error) {

 }];
  • 離散型:
      對應(yīng)的是接下來的YTKNetwork伤靠,離散型最大的特點(diǎn)就是一個網(wǎng)絡(luò)請求對應(yīng)一個單獨(dú)的類范咨,在這個類內(nèi)部封裝請求地址、方式燥撞、參數(shù)座柱、校驗和處理請求回來的數(shù)據(jù),通常代理回調(diào)物舒,需要跨層數(shù)據(jù)傳遞時也使用通知回調(diào)色洞,比較集中,因為數(shù)據(jù)處理都放在內(nèi)部處理了冠胯,返回數(shù)據(jù)的形式(模型化后的數(shù)據(jù)還是其他)不需要控制器關(guān)心火诸,控制器只需要在代理返回的數(shù)據(jù)可以直接對渲染UI,讓Controller更加輕量化荠察。

發(fā)起請求

    NSString *userId = @"1";
    GetUserInfoApi *api = [[GetUserInfoApi alloc] initWithUserId:userId];
    [api start];
    api.delegate = self;

Delegate回調(diào)

- (void)requestFinished:(YTKBaseRequest *)request {
    NSLog(@"----- succeed ---- %@", request.responseJSONObject);
    //Rendering interface
}
- (void)requestFailed:(YTKBaseRequest *)request {
    NSLog(@"failed");
}
YTKNetwork解析

首先看下YTKNetwork的類文件:

YTKNetwork.png

圖解它們之間的調(diào)用關(guān)系,注意還是理順關(guān)系悉盆,看懂這個圖應(yīng)該對源碼的理解沒有太多問題:

Scrren.png
  • YTKBaseRequest:YTKRequest的父類,定義了Request的相關(guān)屬性舀瓢,Block和Delegate。給對外接口默認(rèn)的實現(xiàn),以及公共邏輯航缀。

  • YTKRequest:主要對緩存做處理,更新緩存芥玉、讀取緩存、手動寫入緩存灿巧,是否忽略緩存。這里采用歸檔形式緩存抠藕,請求方式、根路徑盾似、請求地址、請求參數(shù)零院、app版本號溉跃、敏感數(shù)據(jù)拼接再M(fèi)D5作為緩存的文件名,保證唯一性告抄。還提供設(shè)置緩存的保存時長撰茎,主要實現(xiàn)是通過獲取緩存文件上次修改的時刻距離現(xiàn)在的時間和設(shè)置的緩存時長作比較,來判斷是否真正發(fā)起請求打洼,下面是發(fā)起請求的一些邏輯判斷:

- (void)start {
    
    if (self.ignoreCache)   // 如果忽略緩存 --> 網(wǎng)絡(luò)請求
    {
        [self startWithoutCache];
        return;
    }

    // Do not cache download request.
    if (self.resumableDownloadPath)     // 不會緩存下載請求 --> 網(wǎng)絡(luò)請求
    {
        [self startWithoutCache];
        return;
    }

    if (![self loadCacheWithError:nil])     // 從緩存加載出錯 --> 網(wǎng)絡(luò)請求
    {
        [self startWithoutCache];
        return;
    }

    _dataFromCache = YES;   // 從緩存加載數(shù)據(jù)

    dispatch_async(dispatch_get_main_queue(), ^{
        [self requestCompletePreprocessor];
        [self requestCompleteFilter];
        YTKRequest *strongSelf = self;
        [strongSelf.delegate requestFinished:strongSelf];
        if (strongSelf.successCompletionBlock) {
            strongSelf.successCompletionBlock(strongSelf);
        }
        [strongSelf clearCompletionBlock];
    });
}

通過歸檔存儲網(wǎng)絡(luò)請求的數(shù)據(jù):

- (void)saveResponseDataToCacheFile:(NSData *)data {
    if ([self cacheTimeInSeconds] > 0 && ![self isDataFromCache]) {
        if (data != nil) {
            @try {
                // New data will always overwrite old data.
                [data writeToFile:[self cacheFilePath] atomically:YES];

                YTKCacheMetadata *metadata = [[YTKCacheMetadata alloc] init];
                metadata.version = [self cacheVersion];
                metadata.sensitiveDataString = ((NSObject *)[self cacheSensitiveData]).description;
                metadata.stringEncoding = [YTKNetworkUtils stringEncodingWithRequest:self];
                metadata.creationDate = [NSDate date];
                metadata.appVersionString = [YTKNetworkUtils appVersionString];
                [NSKeyedArchiver archiveRootObject:metadata toFile:[self cacheMetadataFilePath]];
            } @catch (NSException *exception) {
                YTKLog(@"Save cache failed, reason = %@", exception.reason);
            }
        }
    }
}

  • YTKNetworkAgent:真正發(fā)起網(wǎng)絡(luò)請求的類龄糊,在addRequest方法里調(diào)用AFN的方法,這塊可以方便的更換第三方庫拟蜻,還包括一些請求取消绎签,插件的代理方法調(diào)用等,所有網(wǎng)絡(luò)請求失敗或者成功都會調(diào)用下面這個方法:
- (void)handleRequestResult:(NSURLSessionTask *)task responseObject:(id)responseObject error:(NSError *)error {
    Lock();
    YTKBaseRequest *request = _requestsRecord[@(task.taskIdentifier)];      // 從dic中獲取request
    Unlock();

    // When the request is cancelled and removed from records, the underlying
    // AFNetworking failure callback will still kicks in, resulting in a nil `request`.
    //
    // Here we choose to completely ignore cancelled tasks. Neither success or failure
    // callback will be called.
    if (!request) {
        return;
    }

    YTKLog(@"Finished Request: %@", NSStringFromClass([request class]));

    NSError * __autoreleasing serializationError = nil;
    NSError * __autoreleasing validationError = nil;

    NSError *requestError = nil;
    BOOL succeed = NO;

    request.responseObject = responseObject;
    if ([request.responseObject isKindOfClass:[NSData class]]) {
        request.responseData = responseObject;
        request.responseString = [[NSString alloc] initWithData:responseObject encoding:[YTKNetworkUtils stringEncodingWithRequest:request]];

        switch (request.responseSerializerType) {
            case YTKResponseSerializerTypeHTTP:
                // Default serializer. Do nothing.
                break;
            case YTKResponseSerializerTypeJSON:
                request.responseObject = [self.jsonResponseSerializer responseObjectForResponse:task.response data:request.responseData error:&serializationError];
                request.responseJSONObject = request.responseObject;
                break;
            case YTKResponseSerializerTypeXMLParser:
                request.responseObject = [self.xmlParserResponseSerialzier responseObjectForResponse:task.response data:request.responseData error:&serializationError];
                break;
        }
    }
    if (error) {
        succeed = NO;
        requestError = error;
    } else if (serializationError) {
        succeed = NO;
        requestError = serializationError;
    } else {
        succeed = [self validateResult:request error:&validationError];
        requestError = validationError;
    }

    if (succeed)     // 請求成功
    {
        [self requestDidSucceedWithRequest:request];
    } else          // 請求失敗
    {
        [self requestDidFailWithRequest:request error:requestError];
    }

    dispatch_async(dispatch_get_main_queue(), ^{
        [self removeRequestFromRecord:request];     // 從dic中移除request
        [request clearCompletionBlock];             // block置nil酝锅,可以打破循環(huán)引用诡必;
    });
}

  • YTKNetworkConfig:配置請求根路徑、DNS地址搔扁。

  • YTKNetworkPrivate:可以理解為一個工具類爸舒,拼接地址,提供加密方法稿蹲,定義分類等扭勉。

  • YTKBatchRequestYTKChainRequest:這是YKTNetwork的兩個高級用法苛聘,批量網(wǎng)絡(luò)請求和鏈?zhǔn)降木W(wǎng)絡(luò)請求涂炎,相當(dāng)于一個存放Request的容器忠聚,先定義下面屬性,

YTKBatchRequest finishedCount來記錄批量請求的完成的個數(shù):

@interface YTKBatchRequest() <YTKRequestDelegate>
@property (nonatomic) NSInteger finishedCount;
@end

每完成一個請求finishedCount++唱捣,直到finishedCount等于所有請求的個數(shù)時才回調(diào)成功两蟀。

#pragma mark - Network Request Delegate

- (void)requestFinished:(YTKRequest *)request {
    _finishedCount++;
    if (_finishedCount == _requestArray.count) {
        [self toggleAccessoriesWillStopCallBack];
        if ([_delegate respondsToSelector:@selector(batchRequestFinished:)]) {
            [_delegate batchRequestFinished:self];
        }
        if (_successCompletionBlock) {
            _successCompletionBlock(self);
        }
        [self clearCompletionBlock];
        [self toggleAccessoriesDidStopCallBack];
        [[YTKBatchRequestAgent sharedAgent] removeBatchRequest:self];
    }
}

YTKChainRequest 給Request綁定一個Index

@interface YTKChainRequest()<YTKRequestDelegate>
@property (assign, nonatomic) NSUInteger nextRequestIndex;
@end

從requestArray數(shù)組中依次取出發(fā)起網(wǎng)絡(luò)請求,同時nextRequestIndex++震缭,只要一個請求失敗則觸發(fā)失敗的回調(diào):

- (void)start {
    if (_nextRequestIndex > 0) {
        YTKLog(@"Error! Chain request has already started.");
        return;
    }

    if ([_requestArray count] > 0) {
        [self toggleAccessoriesWillStartCallBack];
        [self startNextRequest];
        [[YTKChainRequestAgent sharedAgent] addChainRequest:self];
    } else {
        YTKLog(@"Error! Chain request array is empty.");
    }
}


//下一個網(wǎng)絡(luò)請求
- (BOOL)startNextRequest {
    if (_nextRequestIndex < [_requestArray count]) {
        YTKBaseRequest *request = _requestArray[_nextRequestIndex];
        _nextRequestIndex++;
        request.delegate = self;
        [request clearCompletionBlock];
        [request start];
        return YES;
    } else {
        return NO;
    }
}
最后

最近也是看得比寫代碼多赂毯,大都一些源碼和博客,種類也比較泛拣宰,讀懂作者的思路還是收獲頗多党涕,往往有時候會要上好幾遍,每一遍都有些新的收獲巡社,貴在堅持了https://github.com/ShelinShelin/SourceCodeAnalyze膛堤。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末骑祟,一起剝皮案震驚了整個濱河市回懦,隨后出現(xiàn)的幾起案子怯晕,更是在濱河造成了極大的恐慌,老刑警劉巖谭期,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件隧出,死亡現(xiàn)場離奇詭異阀捅,居然都是意外死亡饲鄙,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進(jìn)店門帆谍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來汛蝙,“玉大人,你說我怎么就攤上這事窖剑。” “怎么了苛吱?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵绘雁,是天一觀的道長。 經(jīng)常有香客問我援所,道長,這世上最難降的妖魔是什么挪略? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任杠娱,我火速辦了婚禮摊求,結(jié)果婚禮上刘离,老公的妹妹穿的比我還像新娘。我一直安慰自己茧痕,他們只是感情好踪旷,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布埃脏。 她就那樣靜靜地躺著秋忙,像睡著了一般。 火紅的嫁衣襯著肌膚如雪堵幽。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天努咐,我揣著相機(jī)與錄音殴胧,去河邊找鬼。 笑死竿屹,一個胖子當(dāng)著我的面吹牛灸姊,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播碗誉,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼哮缺,長吁一口氣:“原來是場噩夢啊……” “哼蝴蜓!你這毒婦竟也來了碟绑?” 一聲冷哼從身側(cè)響起格仲,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤凯肋,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后侮东,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體悄雅,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡宽闲,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了娩梨。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡颂龙,死狀恐怖厘托,靈堂內(nèi)的尸體忽然破棺而出稿湿,到底是詐尸還是另有隱情,我是刑警寧澤包斑,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布罗丰,位于F島的核電站萌抵,受9級特大地震影響元镀,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜讨永,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一卿闹、第九天 我趴在偏房一處隱蔽的房頂上張望萝快。 院中可真熱鬧,春花似錦旋恼、人聲如沸氢拥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽审葬。三九已至,卻和暖如春痴荐,著一層夾襖步出監(jiān)牢的瞬間官册,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工鸦难, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留合蔽,地道東北人介返。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓圣蝎,卻偏偏與公主長得像,于是被迫代替她去往敵國和親组去。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評論 2 353

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