CTNetworking 源碼分析

原始粗狂版本

CTNetworking 源碼分析

從最外部的業(yè)務(wù)層開始,一步一步的向底層分析
demo 中FireSingleAPI 類作為業(yè)務(wù)層網(wǎng)絡(luò)請求的入口

  • (void)viewDidAppear:(BOOL)animated
    {
    [super viewDidAppear:animated];
    //開始網(wǎng)絡(luò)請求
    [self.testAPIManager loadData];
    }

//進行網(wǎng)絡(luò)請求的類

  • (TestAPIManager *)testAPIManager
    {
    if (_testAPIManager == nil) {
    _testAPIManager = [[TestAPIManager alloc] init];
    //網(wǎng)絡(luò)請求結(jié)果回調(diào)
    _testAPIManager.delegate = self;
    //網(wǎng)絡(luò)請求參數(shù)代理
    _testAPIManager.paramSource = self;
    }
    return _testAPIManager;
    }

//設(shè)置網(wǎng)絡(luò)請求參數(shù)的代理

pragma mark - CTAPIManagerParamSource

  • (NSDictionary *)paramsForApi:(CTAPIBaseManager *)manager
    {
    NSDictionary *params = @{};

    if (manager == self.testAPIManager) {
    params = @{
    kTestAPIManagerParamsKeyLatitude:@(31.228000),
    kTestAPIManagerParamsKeyLongitude:@(121.454290)
    };
    }
    return params;
    }

//網(wǎng)絡(luò)請求結(jié)果代理

pragma mark - CTAPIManagerCallBackDelegate

//請求成功

  • (void)managerCallAPIDidSuccess:(CTAPIBaseManager *)manager
    {
    if (manager == self.testAPIManager) {
    self.resultLable.text = @"success";
    NSLog(@"%@", [manager fetchDataWithReformer:nil]);
    [self layoutResultLable];
    }
    }

//請求失敗

  • (void)managerCallAPIDidFailed:(CTAPIBaseManager *)manager
    {
    if (manager == self.testAPIManager) {
    self.resultLable.text = @"fail";
    NSLog(@"%@", [manager fetchDataWithReformer:nil]);
    [self layoutResultLable];
    }
    }

以上就是業(yè)務(wù)層關(guān)于CTNetworking 網(wǎng)絡(luò)框架的用法。
使用 TestAPIManager 進行網(wǎng)絡(luò)請求, 設(shè)置請求參數(shù), 獲取請求結(jié)果

TestAPIManager 類
這里詳細看一下 TestAPIManager 是如何進行網(wǎng)絡(luò)請求, 設(shè)置請求參數(shù) 處理請求結(jié)果的
從loadData 開始

  • (NSInteger)loadData
    {
    //獲取請求參數(shù), self.paramSource 的代理一般都是進行網(wǎng)絡(luò)請求的業(yè)務(wù)層類 上面我們已經(jīng)設(shè)置了請求參數(shù),這里直接獲取
    NSDictionary *params = [self.paramSource paramsForApi:self];
    //進行網(wǎng)絡(luò)請求
    NSInteger requestId = [self loadDataWithParams:params];
    return requestId;
    }

查看loadDataWithParams 方法

這個方法,詳細處理了網(wǎng)絡(luò)請求的具體操作

  1. 網(wǎng)絡(luò)請求是否會被攔截,這里出現(xiàn)了網(wǎng)絡(luò)攔截器 的類 CTAPIManagerInterceptor
    使用網(wǎng)絡(luò)攔截器來 判斷網(wǎng)絡(luò)請求參數(shù)是否合法,合法則繼續(xù)網(wǎng)絡(luò)請求,非法則攔截網(wǎng)絡(luò)請求
  2. 查看是否有緩存,如果有緩存 則不進行網(wǎng)絡(luò)請求直接 取本地緩存的數(shù)據(jù)
  3. 沒有本地緩存則從服務(wù)器請求數(shù)據(jù)
  • (NSInteger)loadDataWithParams:(NSDictionary *)params
    {

    NSInteger requestId = 0;
    NSDictionary *apiParams = [self reformParams:params];
    if ([self shouldCallAPIWithParams:apiParams]) {
    //判斷請求參數(shù)是否合法
    if ([self.validator manager:self isCorrectWithParamsData:apiParams]) {
    // shouldLoadFromNative 在TestAPIManager中并沒有實現(xiàn), 而是在TestAPIManager的父類CTAPIBaseManager 中實現(xiàn)了該方法,其實當(dāng) 程序走到這里是 self就是TestAPIManager,
    //所以把shouldLoadFromNative 實現(xiàn)在TestAPIManager 類中和實現(xiàn)在CTAPIBaseManager類中原理是一樣的,此時TestAPIManager 就是CTAPIBaseManager 類
    if ([self.child shouldLoadFromNative]) {//是否加載 本地緩存數(shù)據(jù)
    [self loadDataFromNative];
    }

          // 先檢查一下是否從本篤獲取緩存數(shù)據(jù)
          if ([self shouldCache] && [self hasCacheWithParams:apiParams]) {
              return 0;
          }
          
          // 從服務(wù)器獲取數(shù)據(jù)
          if ([self isReachable]) {//檢查是否聯(lián)網(wǎng),網(wǎng)絡(luò)狀態(tài)
              self.isLoading = YES;//開啟加載狀態(tài)
                  switch (self.child.requestType)
                  {   //GET
                      case CTAPIManagerRequestTypeGet:
                          AXCallAPI(GET, requestId);
                          break;
                      //POST
                      case CTAPIManagerRequestTypePost:
                          AXCallAPI(POST, requestId);
                          break;
                      //PUT
                      case CTAPIManagerRequestTypePut:
                          AXCallAPI(PUT, requestId);
                          break;
                      //DELETE
                      case CTAPIManagerRequestTypeDelete:
                          AXCallAPI(DELETE, requestId);
                      break;
                      default:
                          break;
                  }
          
              NSMutableDictionary *params = [apiParams mutableCopy];
              params[kCTAPIBaseManagerRequestID] = @(requestId);
              [self afterCallingAPIWithParams:params];
              return requestId;
          
          } else {
              [self failedOnCallingAPI:nil withErrorType:CTAPIManagerErrorTypeNoNetWork];
              return requestId;
          }
      } else {
          [self failedOnCallingAPI:nil withErrorType:CTAPIManagerErrorTypeParamsError];
          return requestId;
      }
    

    }
    return requestId;
    }

我們先從服務(wù)器請求數(shù)據(jù)開始,加載本地緩存 后面再分析
當(dāng)網(wǎng)絡(luò)請求成功之后,會調(diào)用方法:successedOnCallingAPI
主要處理事項:

  1. 驗證網(wǎng)絡(luò)數(shù)據(jù)是否合法
  2. 將網(wǎng)絡(luò)數(shù)據(jù)緩存到本地 用CTCachedObject 類去緩存數(shù)據(jù)
  3. 響應(yīng)成功 通知代理(業(yè)務(wù)層)去將網(wǎng)絡(luò)數(shù)據(jù)更新到UI
  4. 響應(yīng)的數(shù)據(jù)出錯 通知代理(業(yè)務(wù)層) 讓UI做失敗處理
  • (void)successedOnCallingAPI:(CTURLResponse *)response
    {
    //網(wǎng)絡(luò)等待狀態(tài)
    self.isLoading = NO;
    self.response = response;
    //是否從本地加載
    if ([self.child shouldLoadFromNative]) {
    if (response.isCache == NO) {
    //將響應(yīng)數(shù)據(jù) 保存到本地
    [[NSUserDefaults standardUserDefaults] setObject:response.responseData forKey:[self.child methodName]];
    [[NSUserDefaults standardUserDefaults] synchronize];
    }
    }
    //將響應(yīng)數(shù)據(jù) 賦值給 self
    if (response.content) {
    self.fetchedRawData = [response.content copy];
    } else {
    self.fetchedRawData = [response.responseData copy];
    }
    //把 response 從響應(yīng)隊列中刪除
    [self removeRequestIdWithRequestID:response.requestId];
    //驗證返回 數(shù)據(jù)的參數(shù)是否正確
    if ([self.validator manager:self isCorrectWithCallBackData:response.content]) {//正確
    //緩存 請求到的數(shù)據(jù)
    if ([self shouldCache] && !response.isCache) {
    [self.cache saveCacheWithData:response.responseData serviceIdentifier:self.child.serviceType methodName:self.child.methodName requestParams:response.requestParams];
    }

      if ([self beforePerformSuccessWithResponse:response]) {
          if ([self.child shouldLoadFromNative]) {//是否從加載本地緩存
              if (response.isCache == YES) {//緩存的response
                  //業(yè)務(wù)層 處理響應(yīng)數(shù)據(jù)
                  [self.delegate managerCallAPIDidSuccess:self];
              }
              if (self.isNativeDataEmpty) {//本地緩存為空
                  //業(yè)務(wù)層 處理響應(yīng)數(shù)據(jù)
                  [self.delegate managerCallAPIDidSuccess:self];
              }
          } else {
              //業(yè)務(wù)層 處理響應(yīng)數(shù)據(jù)
              [self.delegate managerCallAPIDidSuccess:self];
          }
      }
      //獲取到響應(yīng)數(shù)據(jù)之后的處理
      [self afterPerformSuccessWithResponse:response];
    

    } else {
    //響應(yīng)數(shù)據(jù) 不正確
    [self failedOnCallingAPI:response withErrorType:CTAPIManagerErrorTypeNoContent];
    //CTAPIManagerErrorTypeNoContent API請求成功但返回數(shù)據(jù)不正確。如果回調(diào)數(shù)據(jù)驗證函數(shù)返回值為NO,manager的狀態(tài)就會是這個
    }
    }

上面的方法是響應(yīng)數(shù)據(jù)成功后的處理
現(xiàn)在分享響應(yīng)數(shù)據(jù)失敗之后的處理
方法:failedOnCallingAPI
主要處理事項:
根據(jù)不同的錯誤類型 做不同的處理
1.token 過期
2.token 無效
3.無權(quán)限
4.其他錯誤
方法如下:
//針對不同響應(yīng)數(shù)據(jù)失敗類型 分別處理

  • (void)failedOnCallingAPI:(CTURLResponse *)response withErrorType:(CTAPIManagerErrorType)errorType
    {
    self.isLoading = NO;//加載狀態(tài)為 NO
    self.response = response;
    if ([response.content[@"id"] isEqualToString:@"expired_access_token"]) {
    // token 過期
    //發(fā)送 token 過期通知
    [[NSNotificationCenter defaultCenter] postNotificationName:kBSUserTokenInvalidNotification
    object:nil
    userInfo:@{
    kBSUserTokenNotificationUserInfoKeyRequestToContinue:[response.request mutableCopy],
    kBSUserTokenNotificationUserInfoKeyManagerToContinue:self
    }];
    } else if ([response.content[@"id"] isEqualToString:@"illegal_access_token"]) {
    // token 無效,重新登錄
    [[NSNotificationCenter defaultCenter] postNotificationName:kBSUserTokenIllegalNotification
    object:nil
    userInfo:@{
    kBSUserTokenNotificationUserInfoKeyRequestToContinue:[response.request mutableCopy],
    kBSUserTokenNotificationUserInfoKeyManagerToContinue:self
    }];
    } else if ([response.content[@"id"] isEqualToString:@"no_permission_for_this_api"]) {
    //非法權(quán)限
    [[NSNotificationCenter defaultCenter] postNotificationName:kBSUserTokenIllegalNotification
    object:nil
    userInfo:@{
    kBSUserTokenNotificationUserInfoKeyRequestToContinue:[response.request mutableCopy],
    kBSUserTokenNotificationUserInfoKeyManagerToContinue:self
    }];
    } else {
    // 其他錯誤
    self.errorType = errorType;
    //在響應(yīng)列表中刪除 該響應(yīng)事件
    [self removeRequestIdWithRequestID:response.requestId];
    if ([self beforePerformFailWithResponse:response]) {
    // 業(yè)務(wù)層回調(diào) 請求響應(yīng)數(shù)據(jù)失敗方法
    [self.delegate managerCallAPIDidFailed:self];
    }
    [self afterPerformFailWithResponse:response];
    }
    }

CTAPIBaseManager 類就分析到這

比較兩個方法是否相等:
如果遇到兩個同名的方法,怎么樣才能判斷他們是同一個方法呢?
如下:
IMP childIMP = [self.child methodForSelector:@selector(reformParams:)];//代理方法
IMP selfIMP = [self methodForSelector:@selector(reformParams:)];//自身方法

if (childIMP == selfIMP) {//判斷是否是同一個方法
    return params;
}

如何處理緩存。
創(chuàng)建緩存對象CTCachedObject ,對象的屬性有 緩存內(nèi)容,緩存是否為空,緩存是否過期,緩存跟新時間
方法有:帶有緩存內(nèi)容的初始化方法, 更新緩存內(nèi)容的方法

緩存的保存,獲取,刪除等操作, 把這些操作抽象成一個對象 CTCache 。
考慮到 緩存到處都在使用,把他設(shè)為單類。
他有一個 NSCache 對象,可以對 緩存對象CTCachedObject進行增刪改的操作。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,502評論 19 139
  • iOS網(wǎng)絡(luò)架構(gòu)討論梳理整理中。。。 其實如果沒有APIManager這一層是沒法使用delegate的,畢竟多個單...
    yhtang閱讀 5,466評論 1 23
  • iOS開發(fā)系列--網(wǎng)絡(luò)開發(fā) 概覽 大部分應(yīng)用程序都或多或少會牽扯到網(wǎng)絡(luò)開發(fā),例如說新浪微博、微信等,這些應(yīng)用本身可...
    lichengjin閱讀 4,020評論 2 7
  • 第一篇第二篇大概是把下載圖片緩存圖片的這個邏輯走完了,里面涉及好多類。 羅列一下 UIView+WebCache ...
    充滿活力的早晨閱讀 841評論 0 1
  • 對于手工測試而言,軟件測試員談?wù)摳嗟木褪亲詣踊瘻y試了。這二者的差別,無非就是一個是手工測試,一個利用工具來測試。...
    吳小白吃閱讀 978評論 0 1

友情鏈接更多精彩內(nèi)容