在iOS開發(fā)中,網(wǎng)絡是必不可少的一部分,沒有人不知道大名鼎鼎的AFNetwork框架的前翎,因為它提供了非常豐富實用,方便的網(wǎng)絡調用畅涂。使得很多需求都能夠調用已有的方法完成鱼填。但是面對業(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 分別表示請求成功和失敗的回調商模。但是他們缺點重重,最明顯的
- 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)先從緩存中讀取內容,如果緩存讀取成功,就用回調成功block,然后檢查緩存是否過期互墓,過期就從網(wǎng)絡請求數(shù)據(jù)判莉,然后把新的數(shù)據(jù)存入到緩存中锰镀。
- 網(wǎng)絡請求的結果:
- 成功獲得數(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ù)短時間內變化不是很快
的地方。 - 用戶下拉刷新表示重新請求數(shù)據(jù)舱卡,應該強制更新數(shù)據(jù)浮禾,不論本地是否有緩存,但是這部分數(shù)據(jù)應該存入數(shù)據(jù)庫,保證退出后下次進入界面的時候能夠讀取最后更新的緩存。
- 上拉加載更多的時候不讀緩存歪今,因為如果客戶端自己在處理這部分邏輯比較復雜嫉晶,不是說實現(xiàn)起來復雜泊柬,因為對于本地存儲來說調用的是統(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,很可能是個字符串什么烁设,這樣可以按照不同的需求調用類里面的方法得到自己想要的數(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;
/**
* 轉換成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"];
}
//這里調用網(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)絡調用需要自己加麻煩。
- 真正使用起來雞肋深寥,為了完成上述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.太麻煩了则酝,不能忍。
解決方案
- 我想我可以通過給網(wǎng)絡調用添加參數(shù)的形式方式擴展功能闰集,但是放棄了沽讹,因為如果給原來的方法添加一個參數(shù),工程所有的調用的地方都要改返十,實在麻煩妥泉,如果原來的方法保留,復制一下加個參數(shù)工程中會多出很多冗余代碼洞坑,而且參數(shù)過多調用起來也不方便。
- 自定義一個網(wǎng)絡配置類 NetSetting蝇率,包含發(fā)起一次網(wǎng)絡請求所有的控制策略迟杂,將類的對象作為網(wǎng)絡調用的參數(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表示由調用的控制器控制浪腐,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;
//只讀緩存的內容
@property (nonatomic,assign) BOOL justReadCache;//啟動頁廣告會用到
//默認的設置
+(instancetype)noSaveCacheSet;//不寫緩存
+(instancetype)noReadCacheSet;//不讀緩存
+(instancetype)readCacheSet;//讀緩存
+(instancetype)noEncryptPost;
此時AFNetwork封裝的方法包含了兩個部分
- 對外開放的部分顿乒,所有的網(wǎng)絡請求只能調用這個方法發(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).需要在調用者里面繼續(xù)調用蛤售。
});
}
} 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){
//調用manage的 GET方法同上
}
}
公開方法
在需要向網(wǎng)絡獲取新數(shù)據(jù)的時候會把自己的所有參數(shù)傳遞給私有方法
,私有方法
在完成網(wǎng)絡請求的時候會把結果回調給公開方法
,然后公開方法最終把數(shù)據(jù)返回給調用者。隨著業(yè)務的需求邏輯會愈加復雜悍抑,變動更加多樣鳄炉,需要自己編寫代碼的執(zhí)行邏輯。但是這樣的方法使得在很多情況下僅僅更改某幾行代碼就實現(xiàn)了功能搜骡。
應用場景
- 為了方便調用拂盯,所有的控制繼承根控制器,根控制器設置了一個HSNetSetting 屬性记靡。并且懶加載的形式重寫了get方法
-(HSNetSetting*)defaultSet
{
if (_defaultSet == nil) {
_defaultSet =[ [HSNetSetting alloc]init];
//設置默認的配置
}
return _defaultSet;
}
這樣所有的子類谈竿,在有tableview的時候發(fā)起網(wǎng)絡請求的時候直接傳遞self.defaultSet 就可以了,而且在 公開方法
內部在完成后數(shù)據(jù)請求后摸吠,把傳遞的netSetting改了空凸,如果傳遞的緩存策略是讀緩存,請求完成后直接改成不讀緩存寸痢,不用自己手動更改呀洲。
- 前段時間啟動頁加廣告,返回的是json啼止,包含了廣告的停留時間和圖片地址道逗,顯示與否信息。但是啟動頁時間太短献烦,本次廣告應該讀取上一次的滓窍,直接在HSNetSetting中加入了
@property (nonatomic,assign) BOOL justReadCache;//啟動頁廣告會用到
然后自己調整緩存方法的邏輯,用同一個方法實現(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瞧甩,登錄完成后重新調用一次自己剛剛的操作就能解決這問題。
SVProgressHud 網(wǎng)絡請求的時候需要自己總是寫麻煩弥鹦,可以根據(jù)netSetting 配置讓網(wǎng)絡方法內部處理肚逸。但是某些場合不需要出現(xiàn)SVProgressHud爷辙,即使讀取的數(shù)據(jù)是從網(wǎng)絡獲取的也不出現(xiàn),只需要關閉netSetting對SVProgressHud的控制朦促,但是自己也不添加SVProgressHud的控制就時間了膝晾。
緩存時間不由客戶端控制,需要交給服務器控制务冕,無所謂血当,稍微調整網(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塊回調給控制器优构,當然和前面一樣這里要更新緩存數(shù)據(jù)和日期,其他情況不回調給控制器就解決了雁竞。
到此為止一個相對完整的網(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è)務的依賴吗浩。使得通用性更高建芙。