1. 按鈕延時(shí)處理事件有什么應(yīng)用場(chǎng)景顶瞒?
如果你做的是一個(gè)帶有輕微社交功能的APP血筑,這類APP一般都會(huì)有類似“收藏”、“點(diǎn)贊”消恍、“喜愛(ài)”的功能岂昭。
這些功能其實(shí)載體是一個(gè)UIButton,如果你在每次用戶點(diǎn)贊的時(shí)候都發(fā)請(qǐng)求給服務(wù)器狠怨,假如有些用戶“手便宜”约啊,在那里重復(fù)的點(diǎn)擊邑遏,就會(huì)造成一個(gè)請(qǐng)求還沒(méi)回來(lái),有連續(xù)發(fā)送出去好幾個(gè)請(qǐng)求恰矩。
出現(xiàn)這種情況记盒,第一,可能造成服務(wù)器不必要的壓力外傅,這簡(jiǎn)直是必然的纪吮;第二,由于你不確定請(qǐng)求回調(diào)什么時(shí)候回來(lái)萎胰,假如用戶把這個(gè)控制器銷毀了碾盟,你的應(yīng)用就可能奔潰。
這個(gè)場(chǎng)景就可以采用按鈕延時(shí)處理事件來(lái)輕松應(yīng)對(duì)技竟。
2.實(shí)例分析冰肴?
像下面的demo里寫(xiě)的這樣:
JPBtnClickDelay
收藏這類功能的事件鏈?zhǔn)牵河脩酎c(diǎn)擊-->處理點(diǎn)擊 -->發(fā)送請(qǐng)求
正常情況,用戶點(diǎn)擊按鈕榔组,響應(yīng)用戶點(diǎn)擊熙尉, 發(fā)送請(qǐng)求。
當(dāng)使用延時(shí)處理以后(我這里設(shè)定延時(shí)時(shí)長(zhǎng)為1.0Second)瓷患,當(dāng)用戶點(diǎn)擊按鈕以后骡尽,響應(yīng)用戶點(diǎn)擊,但是不是立即發(fā)送請(qǐng)求擅编,而是先檢查一下兩次點(diǎn)擊之間時(shí)間差有沒(méi)有1秒攀细,如果有,再發(fā)送請(qǐng)求爱态,如果沒(méi)有谭贪,不發(fā)送請(qǐng)求。
3锦担、動(dòng)態(tài)添加方法和屬性(hook)俭识?
3.1 runtime是什么?
runtime簡(jiǎn)稱運(yùn)行時(shí)洞渔。OC就是運(yùn)行時(shí)機(jī)制套媚,也就是在運(yùn)行時(shí)候的一些機(jī)制,其中最主要的是消息機(jī)制磁椒。
Objective-C 的 Runtime 是一個(gè)運(yùn)行時(shí)庫(kù)(Runtime Library)堤瘤,它是一個(gè)主要使用 C 和匯編寫(xiě)的庫(kù),為 C 添加了面相對(duì)象的能力并創(chuàng)造了 Objective-C浆熔。這就是說(shuō)它在類信息(Class information) 中被加載本辐,完成所有的方法分發(fā),方法轉(zhuǎn)發(fā),等等慎皱。Objective-C runtime 創(chuàng)建了所有需要的結(jié)構(gòu)體老虫,讓 Objective-C 的面相對(duì)象編程變?yōu)榭赡堋?/p>
3.2 動(dòng)態(tài)添加方法和屬性是什么?
比如說(shuō)茫多,我要給一個(gè)人動(dòng)態(tài)添加一個(gè)“吹牛逼”的屬性祈匙,方法是這樣的。先給人添加一個(gè)分類(Category)地梨,然后在分類里添加一個(gè)屬性菊卷。
注意缔恳,分類是專門(mén)用來(lái)添加方法的宝剖,在分類里使用關(guān)鍵字@property添加屬性,系統(tǒng)是不會(huì)幫我們生成setter-getter方法的歉甚。
所以我們要自己實(shí)現(xiàn)setter-getter方法万细。
在setter方法里使用runtime的以下方法動(dòng)態(tài)添加屬性。
voidobjc_setAssociatedObject(idobject,constvoid*key,idvalue, objc_AssociationPolicy policy)
在getter方法里使用runtime的以下方法動(dòng)態(tài)獲取屬性值纸泄。
idobjc_getAssociatedObject(idobject,constvoid*key)
3.3 方法交換是什么赖钞?
記得我們的每一個(gè)OC對(duì)象都有一個(gè)isa指針嗎?這個(gè)isa就是指向創(chuàng)建實(shí)例對(duì)象的類聘裁。
對(duì)象方法保存到類里面,每個(gè)類里面都有一個(gè)方法列表雪营。
當(dāng)調(diào)用對(duì)象方法的時(shí)候,系統(tǒng)都會(huì)來(lái)到這個(gè)表里查找對(duì)應(yīng)的方法和實(shí)現(xiàn)衡便。
方法映射表.png
所謂的方法交換献起,也就是hook,就是把兩個(gè)方法的實(shí)現(xiàn)給交換了镣陕。就像下面這張圖一谴餐,你調(diào)用eat方法的時(shí)候,就會(huì)去找run方法的實(shí)現(xiàn)呆抑。
hook.png
4.思路分析岂嗓?
我們知道UIButton繼承自UIControl,UIButton的所有處理事件的能力都是它的父類UIControl傳給它的鹊碍。UIControl有這樣一個(gè)方法:
// send the action. the first method is called for the event and is a point at which you can observe or override behavior. it is called repeately by the second.- (void)sendAction:(SEL)action to:(nullableid)target forEvent:(nullableUIEvent*)event;
官方的解釋翻譯過(guò)來(lái)是這樣的:這個(gè)方法用以傳遞事件消息厌殉,是監(jiān)聽(tīng)到事件后最先調(diào)用的方法,并且它是隨著事件的重復(fù)產(chǎn)生而頻繁調(diào)用的侈咕。
所以我們要實(shí)現(xiàn)攔截事件傳遞公罕,重寫(xiě)這個(gè)方法是最優(yōu)解。
5.代碼實(shí)現(xiàn)乎完?
首先為UIControl添加創(chuàng)建分類熏兄,并且在.h文件里添加屬性。
#import@interfaceUIControl(JPBtnClickDelay)/** 延遲時(shí)間 */@property(nonatomic)NSTimeIntervaljp_acceptEventInterval;@end
接下來(lái)來(lái)到.m文件
#import"UIControl+JPBtnClickDelay.h"#import@interfaceUIControl()/** 是否忽略點(diǎn)擊 */@property(nonatomic)BOOLjp_ignoreEvent;@end@implementationUIControl(JPBtnClickDelay)-(void)jp_sendAction:(SEL)action to:(id)target forEvent:(UIEvent*)event{if(self.jp_ignoreEvent)return;if(self.jp_acceptEventInterval>0) {self.jp_ignoreEvent=YES;? ? [selfperformSelector:@selector(setJp_ignoreEvent:) withObject:@(NO) afterDelay:self.jp_acceptEventInterval];}? ? [selfjp_sendAction:action to:target forEvent:event];}-(void)setJp_ignoreEvent:(BOOL)jp_ignoreEvent{? ? objc_setAssociatedObject(self,@selector(jp_ignoreEvent), @(jp_ignoreEvent), OBJC_ASSOCIATION_ASSIGN);}-(BOOL)jp_ignoreEvent{return[objc_getAssociatedObject(self, _cmd, boolValue];}-(void)setJp_acceptEventInterval:(NSTimeInterval)jp_acceptEventInterval{? ? objc_setAssociatedObject(self,@selector(jp_acceptEventInterval), @(jp_acceptEventInterval), OBJC_ASSOCIATION_ASSIGN);}-(NSTimeInterval)jp_acceptEventInterval{return[objc_getAssociatedObject(self, _cmd) doubleValue];}+(void)load{? ? Method sys_Method = class_getInstanceMethod(self,@selector(sendAction:to:forEvent:));? ? Method add_Method = class_getInstanceMethod(self,@selector(jp_sendAction:to:forEvent:));? ? method_exchangeImplementations(sys_Method, add_Method);}@end
6. 分類的使用?
這里有兩個(gè)UIButton的實(shí)例對(duì)象:
[self.normalBtnaddTarget:selfaction:@selector(normalBtnClick) forControlEvents:UIControlEventTouchUpInside];[self.delayBtnaddTarget:selfaction:@selector(delayBtnClick) forControlEvents:UIControlEventTouchUpInside];self.delayBtn.jp_acceptEventInterval=1.0f;
normalBtn不需要有延時(shí)摩桶,就什么也不用管桥状,就和使用系統(tǒng)原生的一樣。
delayBtn需要延時(shí)硝清,給它的jp_acceptEventInterval設(shè)定一個(gè)延時(shí)值辅斟,它自動(dòng)就會(huì)生效。
7. Demo下載芦拿?
請(qǐng)點(diǎn)擊這里去往Github士飒。