iOS過濾重復(fù)請求的設(shè)計

  1. 存在的問題
    最近在做項目的過程中沮明,發(fā)生APP啟動的時候,有些接口會在短時間(例如1s)之內(nèi)重復(fù)調(diào)用多次窍奋,在APP執(zhí)行某些操作時荐健,也時不時出現(xiàn)一個接口短時間內(nèi)調(diào)用多次的情況。究其原因琳袄,有些請求重復(fù)是因為在不同的時機(jī)點(diǎn)都需要請求這個接口江场,但是有時候這些不同時機(jī)點(diǎn),在短時間內(nèi)會一起發(fā)生窖逗,例如如下這種情況:
- (void)applicationDidBecomeActive:(UIApplication *)application {
// 請求接口
}

// 首頁控制器
- (void)viewDidLoad {
// 請求接口
}

我需要在app回到前臺時請求接口址否,在首頁控制器加載完后也需要請求接口,這兩個不同的時機(jī)碎紊,會在APP啟動時短時間內(nèi)都發(fā)生佑附,導(dǎo)致接口重復(fù)請求了。雖然可以針對這種情況寫一些判斷代碼仗考,當(dāng)每發(fā)生類似情況都寫一次判斷就不優(yōu)雅了音同。
還有些重復(fù)請求的情況是代碼一開始設(shè)計沒有考慮那么完善,后面代碼迭代痴鳄,就會導(dǎo)致有些邏輯重復(fù)走幾次瘟斜,接口也重復(fù)請求了。這種改起來就要小心翼翼了痪寻,一不小心就容易改出bug螺句。

  1. 解決問題的思路
    所以仔細(xì)思考后,還是決定統(tǒng)一處理橡类,從請求的底層下手蛇尚,在請求的底層做重復(fù)請求的管理,提供一個過濾方法給業(yè)務(wù)層顾画,業(yè)務(wù)層只需要把原來的請求接口的方法替換成有過濾功能的方法就可以了取劫,不需要改動業(yè)務(wù)邏輯匆笤。

那么應(yīng)該如何設(shè)計這個對外提供的方法呢,我這里設(shè)計為方法會提供一個參數(shù)谱邪,可以設(shè)置過濾時間炮捧,在過濾時間范圍內(nèi),相同的請求只會發(fā)出去一次

@interface EPRequest : YTKRequest<NSCoding>

// url
@property (nonatomic, copy) NSString *url;
// 請求方法
@property (nonatomic, assign) YTKRequestMethod method;
// 請求參數(shù)
@property (nonatomic, copy) NSDictionary *parameters;
...
// 原來的請求方法
- (void)startWithCompletedBlock:(EPRequestCompletedCallBack)callBack;
// 帶過濾功能的請求方法
- (void)startWithFilterDuplicateRequestInDuration:(NSTimeInterval)duration completedBlock:(EPRequestCompletedCallBack)callBack;
...
@end

EPRequest對象惦银,表示一個請求咆课。如果調(diào)用了第二個方法,內(nèi)部就會判斷當(dāng)前的請求是否已經(jīng)開啟了過濾扯俱,如果沒開始书蚪,就發(fā)出這個請求,并且開啟過濾迅栅;如果已經(jīng)開啟了過濾殊校,就根據(jù)當(dāng)前所處的過濾狀態(tài)做相應(yīng)的處理。
這里應(yīng)該注意的是開啟過濾之后读存,又調(diào)用相同url請求的 startWithFilterDuplicateRequestInDuration 方法時为流,不是直接過濾,當(dāng)做沒調(diào)用宪萄,因為原來的請求方法調(diào)用之后是有block回調(diào)的艺谆,會返回調(diào)用結(jié)果榨惰,如果帶過濾功能的請求方法直接無視拜英,豈不是block里面的邏輯沒執(zhí)行了。此時的設(shè)計應(yīng)該是內(nèi)部會過濾請求琅催,但是對于業(yè)務(wù)層來說居凶,是無感知的,我們還是要返回正常結(jié)果藤抡。這個結(jié)果從哪里來呢侠碧,來自一開始的url請求,我們會把請求回來后的結(jié)果記錄下來缠黍,供過濾掉的請求回調(diào)使用弄兜。

所以,根據(jù)過濾時間設(shè)置的定時器時間是否已結(jié)束瓷式,以及一開始發(fā)出的請求結(jié)果是否已經(jīng)返回替饿,我們可以梳理出在請求開啟過濾時,可能存在的四種狀態(tài):
(1)定時器未結(jié)束贸典,結(jié)果未返回
(2)定時器未結(jié)束视卢,結(jié)果已返回
(3)定時器已結(jié)束,結(jié)果未返回
(4)定時器已結(jié)束廊驼,結(jié)果已返回
對于狀態(tài)(1)据过,有需要過濾的請求過來時惋砂,應(yīng)該把這個請求記錄下來,等待第一個發(fā)出的請求結(jié)果的返回绳锅,然后這個結(jié)果就可以直接給該過濾的請求西饵,讓它返回給外部了。
對于狀態(tài)(2)鳞芙,有需要過濾的請求過來時罗标,由于此時第一個發(fā)出的請求的結(jié)果已經(jīng)返回,我們只需要把這個結(jié)果返回給該過濾請求的回調(diào)就可以了积蜻;對于業(yè)務(wù)層來說闯割,相當(dāng)于調(diào)用了接口立即有了結(jié)果。
對于狀態(tài)(3)竿拆,雖然此時定時器已結(jié)束宙拉,但這里我還是設(shè)計為如果一開始的請求結(jié)果未返回,還是視為過濾流程沒有結(jié)束(因為此時可能由于網(wǎng)絡(luò)或者服務(wù)器原因而沒有返回丙笋,短時間內(nèi)再發(fā)一次也是徒勞谢澈,至少也要等超時結(jié)果返回再發(fā));此時也會記錄該請求御板,等待第一個發(fā)出的請求結(jié)果返回锥忿,然后這個結(jié)果給該過濾的請求(超時結(jié)果也算返回的結(jié)果),然后就進(jìn)入了狀態(tài)(4)怠肋。
對于狀態(tài)(4)敬鬓,顯然狀態(tài)(1)(2)(3)最后都會進(jìn)入狀態(tài)(4),表示這個過濾流程已經(jīng)結(jié)束了笙各。此時有需要過濾的請求過來時钉答,說明這是第一個請求,不需要過濾杈抢,這是一個真正會發(fā)出的請求数尿,并且設(shè)置了定時器,開啟了過濾的流程惶楼。

根據(jù)如上的狀態(tài)分析右蹦,以及每個狀態(tài)需要什么操作,畫出的三個流程圖如下所示:

image.png

image.png

這樣這個過濾請求功能的整體設(shè)計就出來了歼捐。但是這里有一點(diǎn)需要注意何陆,就是外部調(diào)用過濾請求的方法時,可能處于不同的線程窥岩,也就是說在內(nèi)部代碼的處理上甲献,會涉及到線程安全的處理。具體來說颂翼,我們應(yīng)該在改變狀態(tài)的代碼上加一個鎖晃洒。為什么呢慨灭,以狀態(tài)(3)和流程3來舉例,假如有一個過濾請求進(jìn)來球及,判斷處于狀態(tài)(3)氧骤,此時這個請求會被記錄,等待結(jié)果返回時應(yīng)用該結(jié)果吃引,但此時流程3在并行執(zhí)行筹陵;該操作(請求結(jié)果返回并將所有記錄的請求取出,將請求結(jié)果回調(diào)給它們)發(fā)生在過濾請求的狀態(tài)(3)判斷之后镊尺,請求被記錄之前朦佩,那么就會導(dǎo)致這個請求雖然被記錄,但是永遠(yuǎn)不會有回調(diào)的時候庐氮。
所以语稠,狀態(tài)判斷的相關(guān)代碼是可以在多個線程同時執(zhí)行的,但是狀態(tài)變更的代碼跟狀態(tài)判斷代碼弄砍,狀態(tài)變更代碼是互斥的仙畦;這個鎖應(yīng)該是一個多讀單寫的鎖
CGD有一個鎖可以實現(xiàn)該功能:

    // 初始化隊列
    dispatch_queue_t queue = dispatch_queue_create("rw_queue", DISPATCH_QUEUE_CONCURRENT);
    // 讀
    dispatch_async(queue, ^{
        
    });
    // 寫
    dispatch_barrier_async(queue, ^{
        
    });

注意 dispatch_barrier_async 傳入的隊列必須是一個自己創(chuàng)建的并發(fā)隊列音婶,不能是全局的并發(fā)隊列(如果傳入全局的并發(fā)隊列慨畸,效果等同于調(diào)用了dispatch_async)。

至此衣式,整個設(shè)計思路就是這樣了寸士,由于具體實現(xiàn)代碼是根據(jù)我們的項目的請求類定制的,所以就不貼出來了瞳收。功能完成后碉京,上線1個月厢汹,完美運(yùn)行hahaha~螟深。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市烫葬,隨后出現(xiàn)的幾起案子界弧,更是在濱河造成了極大的恐慌,老刑警劉巖搭综,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件垢箕,死亡現(xiàn)場離奇詭異,居然都是意外死亡兑巾,警方通過查閱死者的電腦和手機(jī)条获,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蒋歌,“玉大人帅掘,你說我怎么就攤上這事委煤。” “怎么了修档?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵碧绞,是天一觀的道長。 經(jīng)常有香客問我吱窝,道長讥邻,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任院峡,我火速辦了婚禮兴使,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘照激。我一直安慰自己鲫惶,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布实抡。 她就那樣靜靜地躺著欠母,像睡著了一般。 火紅的嫁衣襯著肌膚如雪吆寨。 梳的紋絲不亂的頭發(fā)上赏淌,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天,我揣著相機(jī)與錄音啄清,去河邊找鬼六水。 笑死,一個胖子當(dāng)著我的面吹牛辣卒,可吹牛的內(nèi)容都是我干的掷贾。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼荣茫,長吁一口氣:“原來是場噩夢啊……” “哼想帅!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起啡莉,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤港准,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后咧欣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體浅缸,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年魄咕,在試婚紗的時候發(fā)現(xiàn)自己被綠了衩椒。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖毛萌,靈堂內(nèi)的尸體忽然破棺而出梢什,到底是詐尸還是另有隱情,我是刑警寧澤朝聋,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布嗡午,位于F島的核電站,受9級特大地震影響冀痕,放射性物質(zhì)發(fā)生泄漏荔睹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一言蛇、第九天 我趴在偏房一處隱蔽的房頂上張望僻他。 院中可真熱鬧,春花似錦腊尚、人聲如沸吨拗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽劝篷。三九已至,卻和暖如春民宿,著一層夾襖步出監(jiān)牢的瞬間娇妓,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工活鹰, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留哈恰,地道東北人。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓志群,卻偏偏與公主長得像着绷,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子锌云,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345

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