iOS 工作中封裝通用性網(wǎng)絡請求框架

在iOS開發(fā)中,網(wǎng)絡是必不可少的一部分,沒有人不知道大名鼎鼎的AFNetwork框架的,因為它提供了非常豐富實用,方便的網(wǎng)絡調(diào)用。使得很多需求都能夠調(diào)用已有的方法完成。但是面對業(yè)務需求,如何合理的將AFNetwork近一步封裝能夠更加方便的完成業(yè)務需求卻是需要好好考慮的。以下根據(jù)自己的工作經(jīng)歷中對AFNetwork的封裝。

一、首次接觸


自己在A公司的時候剛剛接觸iOS不久,看到的工程中已經(jīng)存在的封裝是這樣子的

+ (void)postJSONWithUrl:(NSString *)urlStr parameters:(NSMutableDictionary *)dic success:(void (^)(id responseObject))success fail:(void (^)())fail;
+ (void)JSONDataWithUrl:(NSString *)url andDic:(NSMutableDictionary *)dic success:(void (^)(id json))success fail:(void (^)(id error))fail;

這兩個函數(shù)很簡單,就是發(fā)起一個網(wǎng)絡請求,使用的時候urlStr基本上就是固定的用宏來定義的。dic就是傳遞的字典.success 和fail 分別表示請求成功和失敗的回調(diào)。但是他們?nèi)秉c重重,最明顯的

  • post和get應該可以用一個方法來實現(xiàn)的。
  • urlStr每次都寫都要寫太麻煩。

二、接觸正規(guī)需求


后來就是現(xiàn)在的公司了,因為業(yè)務需求比較正規(guī)了,iOS客戶端又是剛剛開始研發(fā),所以跟我提了一系列需求,讓我封裝一套通用網(wǎng)絡方法,相信這也是大部分互聯(lián)網(wǎng)公司的需求吧,只是我以前見識太少。

  • 網(wǎng)絡請求的數(shù)據(jù)要能夠緩存,但是也可以不進行緩存,緩存的數(shù)據(jù)還要可以設置緩存時間。
  • 對于每次網(wǎng)絡請求要優(yōu)先從緩存中讀取內(nèi)容,如果緩存讀取成功,就用回調(diào)成功block,然后檢查緩存是否過期,過期就從網(wǎng)絡請求數(shù)據(jù),然后把新的數(shù)據(jù)存入到緩存中。
  • 網(wǎng)絡請求的結(jié)果:
    • 成功獲得數(shù)據(jù)
    • 請求數(shù)據(jù)失敗、返回特殊狀態(tài)碼。(用戶沒有登陸,已經(jīng)是最新數(shù)據(jù)等等)

緩存的應用場景主要是tableview加載數(shù)據(jù),其他的場景也適合,以下以tableview為例

  • 首次進入一個界面tableview的數(shù)據(jù)應該先讀取緩存,主要防止沒有網(wǎng)絡或者網(wǎng)絡不好的情況下用戶等待數(shù)據(jù)時間過長或者沒有數(shù)據(jù)用戶體驗不好,主要用于某些tableview請求的數(shù)據(jù)量很大,或者數(shù)據(jù)短時間內(nèi)變化不是很快的地方。
  • 用戶下拉刷新表示重新請求數(shù)據(jù),應該強制更新數(shù)據(jù),不論本地是否有緩存,但是這部分數(shù)據(jù)應該存入數(shù)據(jù)庫,保證退出后下次進入界面的時候能夠讀取最后更新的緩存。
  • 上拉加載更多的時候不讀緩存,因為如果客戶端自己在處理這部分邏輯比較復雜,不是說實現(xiàn)起來復雜,因為對于本地存儲來說調(diào)用的是統(tǒng)一的一個方法,主要是因為如果用戶下拉刷新需要強制更新數(shù)據(jù),不論本地有無緩存,都要從服務器請求最新數(shù)據(jù)。那么問題來了,如果我把用戶上拉加載更多時候的數(shù)據(jù)也存入本地,假設用戶上拉加載了5頁數(shù)據(jù),然后又強制下拉刷新了一下,這個時候tableview顯示的是最新的第一頁數(shù)據(jù),如果接著上拉加載更多我應該是讀取緩存還是直接發(fā)起網(wǎng)絡請求呢。毫無疑問應該直接發(fā)起請求,因為下拉強制刷新已經(jīng)導致了第一頁為最新數(shù)據(jù),如果第2-5頁數(shù)據(jù)緩存沒有過期并且服務器數(shù)據(jù)確實變化了的話,客戶端將得不到新的數(shù)據(jù),所以僅僅第一次進入tableview界面,請求第一頁數(shù)據(jù)的時候要讀緩存。
  • 根本不用緩存的地方主要是某些數(shù)據(jù)變化非???,或者發(fā)起的網(wǎng)絡請求是一次性的操作,比如收藏某個題目等等。

根據(jù)上面的需求,我首先想到用sqlite作為緩存的數(shù)據(jù)庫,面對一個完整的url和url請求得到的json字符串提供增刪改查,用了一天時間我封裝了屬于工程的數(shù)據(jù)庫。提供了如下接口

/**
 *  插入一個json緩存數(shù)據(jù)
 *  @param key         鍵
 *  @param value       數(shù)據(jù)
 *  @param expire_time 過期時間
 */
+ (void)insertJonsCacheStringWithKey:(NSString *)key andWithValue:(NSString*) value andWithExpireTime:(NSInteger )expire_time;

/**
 *  根據(jù)JSON緩存獲取一個包含對JSON數(shù)據(jù)操作的對象
 *  @param key 鍵
 *  @return --- JsonCacheData類型的可以對提取的json處理
 */
+(JsonCacheData *)queryJsonCacheTableWithKey:(NSString *)key;

/**
 *  插入的值是字典類型
 *  @param key         鍵
 *  @param value       值
 *  @param expire_time 過期時間
 */
+ (void)insertJsonCacheDictWithKey:(NSString *)key andWithValue:(NSDictionary*) value andWithExpireTime:(NSInteger )expire_time;
/**
 *  刪除數(shù)據(jù)緩存
 *  @param key
 */
+ (BOOL)deleteJsonCacheDictWithKey:(NSString *)key;

并且創(chuàng)建了一個JsonCacheData對象存儲數(shù)據(jù)庫的數(shù)據(jù),因為有的時候存入數(shù)據(jù)庫的不一定是json,很可能是個字符串什么,這樣可以按照不同的需求調(diào)用類里面的方法得到自己想要的數(shù)據(jù)類型.

@interface JsonCacheData : NSObject
//數(shù)據(jù)存入數(shù)據(jù)庫的時間戳
@property (nonatomic,assign) NSInteger saveTime;
//緩存有效期長度
@property (nonatomic,assign) NSInteger expireTime;
//從數(shù)據(jù)庫返回的字符串信息
@property (nonatomic,strong) NSString * jsonCache;
-(BOOL)isExpire;
/**
 *  轉(zhuǎn)換成JSON類型
 *  @return JSON數(shù)據(jù)的字典
 */
-(NSDictionary *)JsonData;
@end

AFNet封裝完成后函數(shù)如下

/**
 *  網(wǎng)絡請求數(shù)據(jù)的方法,可以進行緩存
 *  @param param     上傳請求的參數(shù)
 *  @param success   獲取的json數(shù)據(jù),可能是網(wǎng)絡請求的,也可能是本地緩存的
 *  @param codeError 網(wǎng)絡返回數(shù)據(jù)的錯誤碼
 *  @param fail      網(wǎng)絡連接失敗
 *  @param minutes   緩存的有效時長
 */
+(void)loadDataWithCache:(NSDictionary *) param NetBlock:(void (^)(NSDictionary *json))success ErrorCode :(void(^)(int errorCode))codeError Fail:(void(^)())fail cacheTime:(int) minutes
{
    NSMutableDictionary * dict = [NSMutableDictionary dictionaryWithDictionary:param];
    NSString * sqliteKey = [HSNetUrlProcess createSqliteKeyWithParm:dict];
    JsonCacheData * data = [SQLiteJsonCache queryJsonCacheTableWithKey:sqliteKey];
    //緩存存在并且,緩存時間大于0表示要求讀取緩存的
    if (data.jsonCache.length>0&&minutes>0)
    {
        if (data.jsonCache.length>0) { //如果緩存存在, 有緩存就顯示,并且緩存時間要大于0否則就沒有意義
            if (success!=nil) {
                success(data.JsonData);
            }
            if (data.isExpire) {
                if ([data.JsonData[@"version"]length]>0) {
                    dict[@"version"]=data.JsonData[@"version"];
                }
                //這里調(diào)用網(wǎng)絡方法請求數(shù)據(jù)
                [AFNetworkTool JSONDataWithDic:dict success:^(id json) {
                    if (json!=nil) {
                        //加密
                        [SQLiteJsonCache insertJsonCacheDictWithKey:sqliteKey andWithValue:json andWithExpireTime:60*minutes];
                        success(json);
                    }
                } codeError:^(int errorCode) {
                    if (codeError!=nil) {
                        if (errorCode==NetCode_RESULT_NEWEST) { //更新緩存的時間,已經(jīng)是最新數(shù)據(jù)
                            [SQLiteJsonCache insertJsonCacheDictWithKey:sqliteKey andWithValue:data.JsonData andWithExpireTime:minutes*60];
                        }
                        codeError(errorCode);
                    }
                } fail:^{
                    
                    if (fail!=nil) {
                        fail();
                    }
                }];
            }
        }
    }
    else //這里是不讀取緩存
    {
        [AFNetworkTool JSONDataWithDic:dict success:^(id json) {
            if (minutes!=CACHE_NO_SAVE) { // CACHE_NO_SAVE = -1 表示不需要存入數(shù)據(jù)庫。
                [SQLiteJsonCache insertJsonCacheDictWithKey:sqliteKey andWithValue:json andWithExpireTime:minutes*60];
            }
            success(json);
        } codeError:^(int errorCode) {
            if (codeError!=nil) {
                codeError(errorCode);
            }
        } fail:^{
            if (fail!=nil) {
                fail();
            }
        }];
    }
}

傳遞的cachetime有以下類型

CACHE_NOSAVE CACHE_NOREAD CACHE_READ_NORMAL
-1 0 10
不讀緩存 不讀取緩存,但是要將獲取的數(shù)據(jù)按照CACHE_READ_NO有效期存儲 大部分情況下緩存有效期

上面的封裝也不太好,但是時間太緊迫,趕鴨子上架,而且都等著用我寫的網(wǎng)絡方法呢,所有就先這樣了,在使用的時候暴露了很多問題:

  • 網(wǎng)絡請求有的時候需要控制SVProgressHud的顯示,但是很多場合用,要么多了,要么少了,每次網(wǎng)絡調(diào)用需要自己加麻煩。
  • 真正使用起來雞肋,為了完成上述tableview請求數(shù)據(jù)的需求。我們只能這樣做
    self.cacheTime = CACHE_NO_NORMAL;
    [AFNetworkTool loadDataWithCache:dict NetBlock:^(NSDictionary *json) {
        self.cacheTime = CACHE_NO_READ;
        //process data
    } ErrorCode:^(int errorCode) {
    } Fail:^{
    } cacheTime:self.cacheTime];

用一個全局變量保存緩存策略然后請求數(shù)據(jù)后在改,一個界面如果存在兩個同的網(wǎng)絡請求都用到緩存,就要定義兩個cachetime.太麻煩了,不能忍。


解決方案

  1. 我想我可以通過給網(wǎng)絡調(diào)用添加參數(shù)的形式方式擴展功能,但是放棄了,因為如果給原來的方法添加一個參數(shù),工程所有的調(diào)用的地方都要改,實在麻煩,如果原來的方法保留,復制一下加個參數(shù)工程中會多出很多冗余代碼,而且參數(shù)過多調(diào)用起來也不方便。
  2. 自定義一個網(wǎng)絡配置類 NetSetting,包含發(fā)起一次網(wǎng)絡請求所有的控制策略,將類的對象作為網(wǎng)絡調(diào)用的參數(shù),網(wǎng)絡如何發(fā)起完全根據(jù)傳遞的NetSetting對象的設置
    所以最終經(jīng)過我的不斷修改一個能夠適用于所有需求的網(wǎng)絡請求成功了。

NetSetting類包含了所有的設置,包括請求方式緩存策略,是否加密,是否自己單獨控制SVProgressHud的顯示。

typedef NS_ENUM(int, HSCacheTime) {
    
    HSCacheNoRead = 0,
    HSCacheNoSave = -1,
    HSCacheNormal = 5,
    HSCacheOneMinute = 1,
    HSCacheOneDay = 24*60,
};
typedef enum
{
    NetMethodPOST = 0,
    NetMethodGET = 1,
}NetMethod;
@interface HSNetSetting : NSObject
//加載動畫控制方式,yes表示由調(diào)用的控制器控制,NO表示有AFNetWork類控制
@property (nonatomic,assign) BOOL isCtrlHub;
//緩存的策略
@property (nonatomic,assign) HSCacheTime cachePolicy;
//是否加密
@property (nonatomic,assign) BOOL isEncrypt;
//獲取數(shù)據(jù)的方式,get請求或者post請求
@property (nonatomic,assign) NetMethod askMethod;
//只讀緩存的內(nèi)容
@property (nonatomic,assign) BOOL justReadCache;//啟動頁廣告會用到
//默認的設置
+(instancetype)noSaveCacheSet;//不寫緩存
+(instancetype)noReadCacheSet;//不讀緩存
+(instancetype)readCacheSet;//讀緩存
+(instancetype)noEncryptPost;

此時AFNetwork封裝的方法包含了兩個部分

  • 對外開放的部分,所有的網(wǎng)絡請求只能調(diào)用這個方法發(fā)起。改方法會根據(jù)netSetting的配置去合理的處理網(wǎng)絡請求的細節(jié)。
//這是對外提供的方法
+(void)HVDataCache:(NSDictionary *) param NetBlock:(void (^)(NSDictionary *json))success
                                        ErrorCode :(void(^)(int errorCode))codeError
                                              Fail:(void(^)())fail
                                           Setting:(HSNetSetting*)netSettting
{
    HSNetSetting * set = netSettting ;
    if (set == nil ) {
        set = [HSNetSetting noSaveCacheSet];//默認
        set.isEncrypt = YES;
    }
    NSMutableDictionary * dict = [NSMutableDictionary dictionaryWithDictionary:param];
    NSString * sqliteKey = [HSNetUrlProcess createSqliteKeyWithParm:dict];
    JsonCacheData * data = [SQLiteJsonCache queryJsonCacheTableWithKey:sqliteKey];
    //讀緩存,后面的null是服務器最近返回的錯誤數(shù)據(jù),
    if (data.jsonCache.length>0&&(set.cachePolicy>HSCacheNoRead||set.justReadCache==YES))
    {
        if (data.jsonCache.length>0) { //如果緩存存在, 有緩存就顯示,并且緩存時間要大于0否則就沒有意義
            if (success!=nil) {
                if (data.isExpire==NO) {
                     set.cachePolicy = HSCacheNoRead;
                }
                success(data.JsonData);
            }
            if (data.isExpire) { //要把version帶上,服務器用于md5計算,是否返回數(shù)據(jù)
                if ([data.JsonData[@"version"]length]>0) {
                    dict[@"version"]=data.JsonData[@"version"];
                }
                int cache_span = [data.JsonData[@"cache_span"]intValue];
                cache_span = cache_span>24*60?60:cache_span;
                set.cachePolicy = cache_span;
                [AFNetworkTool HVJSONDataWithDic:dict success:^(id json) {
                    //加密
                    [SQLiteJsonCache insertJsonCacheDictWithKey:sqliteKey andWithValue:json andWithExpireTime:set.cachePolicy*60];
                     set.cachePolicy = HSCacheNoRead;
                    if (set.justReadCache==NO) {
                        success(json);
                    }
                } codeError:^(int errorCode) {
                    if (codeError!=nil) {
                        if (errorCode==NetCode_RESULT_NEWEST) { //更新緩存的時間
                            [SQLiteJsonCache insertJsonCacheDictWithKey:sqliteKey andWithValue:data.JsonData andWithExpireTime:cache_span *60];
                        }
                        codeError(errorCode);
                    }
                } fail:^{
                    if (fail!=nil) {
                        fail();
                    }
                } Setting:set];
            }
        }
    }
    else
    {
        if (!set.isCtrlHub) {
            [SVProgressHUD show];
        }
        [AFNetworkTool HVJSONDataWithDic:dict success:^(id json) {
            if (!set.isCtrlHub) {
                [SVProgressHUD dismiss];
            }
            set.cachePolicy =  [json[@"cache_span"]intValue];
            if (set.cachePolicy!=HSCacheNoSave) {//判斷是不是  不存緩存
                 set.cachePolicy = HSCacheNoRead;
                [SQLiteJsonCache insertJsonCacheDictWithKey:sqliteKey andWithValue:json andWithExpireTime:set.cachePolicy*60];
            }
            if(set.justReadCache==NO)
            {
                success(json);
            }
            else
            {
                codeError(NetCode_NO_OLDDATA);//
            }
            
        } codeError:^(int errorCode) {
            if (codeError!=nil) {
                codeError(errorCode);
            }
            if (!set.isCtrlHub) {
                [SVProgressHUD dismiss];
            }
        } fail:^{
            
            [SVProgressHUD dismiss];
            if (fail!=nil) {
            fail();
        }
        } Setting:set];
    }
}

非對外開放的部分,該方法用于對外開放的方法的使用。

+ (void)HVJSONDataWithDic:(NSDictionary *)paramDict success:(void (^)(id json))success codeError:(void(^)(int errorCode))codeError  fail:(void (^)())fail Setting:(HSNetSetting*)netSettting
{
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    manager.requestSerializer.timeoutInterval = 10.f;//設置請求超時的時間
    manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"text/html",@"image/png",@"image/jpg", nil];
    //[manager.requestSerializer setValue:@"headers" forHTTPHeaderField:@"Referer: http://www.vyanke.com\n"];
    manager.responseSerializer = [AFHTTPResponseSerializer serializer];
    NSMutableDictionary * dict = [NSMutableDictionary dictionaryWithDictionary:paramDict];
    NSString * url;
    NSDictionary * param;
    //拼接將要訪問的URL地址
    NSDictionary * dict2 = [HSNetUrlProcess createCompDictWithParm:dict];
    NSString * value = [dict2 mj_JSONString];
    url = [HSNetUrlProcess createHeadURL:dict];
    NSLog(@"URL是%@",url);
    if (netSettting.isEncrypt==YES) {
        param = @{@"param":[EncryProcess textEncrypt:value]};
        NSLog(@"加密后完整參數(shù)%@",[EncryProcess textEncrypt:value]);
    }
    else
    {
        param = dict2;
    }
    if (netSettting.askMethod == NetMethodPOST) {
        
            [manager POST:url parameters:param progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
            //解密處理
            NSString * str = [[NSString alloc]initWithData:responseObject encoding:NSUTF8StringEncoding];
            if (netSettting.isEncrypt) {
                str = [EncryProcess textDecrypt:str];
                NSLog(@"解密后的參數(shù)=%@",str);
            }
            NSData *jsonData = [str dataUsingEncoding:NSUTF8StringEncoding];
            NSError *err;
            NSDictionary *JSON = [NSJSONSerialization JSONObjectWithData:jsonData
                                                                 options:NSJSONReadingMutableContainers
                                                                   error:&err];
            int code = [JSON[@"state"]intValue];
            if (success&&code==NetCode_NETWORK_SUCCESS) {
                success(JSON);
                return;
            }
            switch (code) {
                case NetCode_TOKEN_EXPIRE_ERROR://{
                {
                    [FLLoginDataModel currentUser].isLogin = NO;
                    [HSCoverView showMessage:JSON[@"msg"] finishBlock:nil];
                    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                        [SVProgressHUD dismiss];//消失
                        HSLoginController * log = [[HSLoginController alloc]init];
                        log.hidesBottomBarWhenPushed =YES;
                        log.finishLoginBlock = ^{
                                    [AFNetworkTool HVJSONDataWithDic:paramDict success:success codeError:codeError fail:fail Setting:netSettting];
                            
                        };
                        if (![[HSTool getCurrentVC] isKindOfClass:[HSLoginController class]]) {
                            
                            [[HSTool getCurrentVC].navigationController pushViewController:log animated:YES];
                        }
    
                    });
                }
                default:
                    [HSCoverView showMessage:JSON[@"msg"] finishBlock:nil];

            }
                
            if (code!= NetCode_NETWORK_SUCCESS&&codeError!=nil) {
                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                    codeError(code);//這里必須寫,否則tableView在使用本類的時候無法停止刷新。一直處于刷新狀態(tài).需要在調(diào)用者里面繼續(xù)調(diào)用。
                });
            }
            
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            
            NSString * tips = [NSString stringWithFormat:@"%ld %@",(long)error.code,error.userInfo[@"NSLocalizedDescription"]];
            NSData * data = error.userInfo[@"com.alamofire.serialization.response.error.data"];
            NSString * message = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
            NSLog(@"服務器的錯誤原因:%@",message);
            [HSCoverView showMessage:tips finishBlock:nil];
            if (fail) {
                fail(error);
            }
        }];
    }
    else if(netSettting.askMethod ==NetMethodGET){
        //調(diào)用manage的 GET方法同上
    }
}

公開方法在需要向網(wǎng)絡獲取新數(shù)據(jù)的時候會把自己的所有參數(shù)傳遞給私有方法,私有方法在完成網(wǎng)絡請求的時候會把結(jié)果回調(diào)給公開方法,然后公開方法最終把數(shù)據(jù)返回給調(diào)用者。隨著業(yè)務的需求邏輯會愈加復雜,變動更加多樣,需要自己編寫代碼的執(zhí)行邏輯。但是這樣的方法使得在很多情況下僅僅更改某幾行代碼就實現(xiàn)了功能。

應用場景

  • 為了方便調(diào)用,所有的控制繼承根控制器,根控制器設置了一個HSNetSetting 屬性。并且懶加載的形式重寫了get方法
-(HSNetSetting*)defaultSet
{
      if (_defaultSet == nil) {  
            _defaultSet =[ [HSNetSetting alloc]init];
          //設置默認的配置
       }
      return _defaultSet;
}

這樣所有的子類,在有tableview的時候發(fā)起網(wǎng)絡請求的時候直接傳遞self.defaultSet 就可以了,而且在 公開方法內(nèi)部在完成后數(shù)據(jù)請求后,把傳遞的netSetting改了,如果傳遞的緩存策略是讀緩存,請求完成后直接改成不讀緩存,不用自己手動更改。

  • 前段時間啟動頁加廣告,返回的是json,包含了廣告的停留時間和圖片地址,顯示與否信息。但是啟動頁時間太短,本次廣告應該讀取上一次的,直接在HSNetSetting中加入了
@property (nonatomic,assign) BOOL justReadCache;//啟動頁廣告會用到

然后自己調(diào)整緩存方法的邏輯,用同一個方法實現(xiàn)需求。

  • app 不是強制要求用戶登錄的,但是進行某些功能要登錄。比如:在A界面點擊按鈕Push到B界面,但是B界面需要用戶登錄后才能獲得正常信息。否則服務器返回消息提示用戶登錄,所以會在B界面push到登錄頁,登錄完成后pop到B界面,然而剛才B頁面沒有數(shù)據(jù),如果不是tableview或者沒有下拉加載功能,只能返回A,然后再push到B界面。用戶體驗很不好,在網(wǎng)絡封裝方法中提示用戶登錄時加入下面代碼:
HSLoginController * log = [[HSLoginController alloc]init];
log.finishLoginBlock = ^{
          [AFNetworkTool HVJSONDataWithDic:paramDict success:success codeError:codeError fail:fail Setting:netSettting];
                        };
  if (![[HSTool getCurrentVC] isKindOfClass:[HSLoginController class]]) {
            [[HSTool getCurrentVC].navigationController pushViewController:log animated:YES];
    }

給登錄控制器加一個block,登錄完成后重新調(diào)用一次自己剛剛的操作就能解決這問題。

  • SVProgressHud 網(wǎng)絡請求的時候需要自己總是寫麻煩,可以根據(jù)netSetting 配置讓網(wǎng)絡方法內(nèi)部處理。但是某些場合不需要出現(xiàn)SVProgressHud,即使讀取的數(shù)據(jù)是從網(wǎng)絡獲取的也不出現(xiàn),只需要關閉netSetting對SVProgressHud的控制,但是自己也不添加SVProgressHud的控制就時間了。

  • 緩存時間不由客戶端控制,需要交給服務器控制,無所謂,稍微調(diào)整網(wǎng)絡方法邏輯就能完成(就是現(xiàn)在這樣的)。

  • 有些界面數(shù)據(jù)更新并不一定是非常頻繁的,但是需要更新的時候就必須更新,比如一個界面包含了用戶獲得的積分,在用戶消費了積分后,再次進入這個界面必須刷新數(shù)據(jù)。把這種類型的界面設置不緩存,并且在viewwillappear
    中發(fā)送請求然后刷新UI無疑能解決問題。但是并不好,一個是因為請求太頻繁了,浪費流量,如果數(shù)據(jù)比較多,網(wǎng)絡比較慢,用戶體驗很不好。但是通過上面介紹的緩存策略,將緩存時間改成一個極小的值,比如1秒,就能有效解決問題。因為如果數(shù)據(jù)沒有發(fā)生變化雖然發(fā)起請求,服務器只返回簡單提示:客戶端的數(shù)據(jù)是最新的,請求的數(shù)據(jù)很快。但是還是不好。因為數(shù)據(jù)雖然在本地,但是UI還是會刷新一下,就是會閃一下。解決這樣的問題依然不難,可以在網(wǎng)絡配置類加一種類型:只要最新的數(shù)據(jù),在本地有緩存的情況下,僅僅在緩存過期并且數(shù)據(jù)確實發(fā)生變化的時候才把最新的數(shù)據(jù)用success塊回調(diào)給控制器,當然和前面一樣這里要更新緩存數(shù)據(jù)和日期,其他情況不回調(diào)給控制器就解決了。

到此為止一個相對完整的網(wǎng)絡請求的框架完成,隨著需求的一步步明確,自己分析的透徹,才有了現(xiàn)在形勢。其實可以更進一步優(yōu)化,可以在數(shù)據(jù)庫和數(shù)據(jù)之間在加一層緩存,也許會更好。自己后來看了一本叫做《圖解http協(xié)議》的書,發(fā)現(xiàn)里面好多緩存策略和上面介紹的很符合,所以以后可以參考下http協(xié)議里面的緩存策略的來擴展網(wǎng)絡類。


更新:
實現(xiàn)代碼地址:https://github.com/xiaobai1993/HSNetTest,以后會逐步完善,減少對當前項目業(yè)務的依賴。使得通用性更高。

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

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

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