好吧,今天中午剛吃完飯坚嗜,屁股還沒坐穩(wěn)呢,CTO就跑過來問我诗充,小姑涼啊苍蔬、掃碼購這塊有bug啊,如果我狂點(diǎn)蝴蜓、碟绑、、茎匠、格仲、、去結(jié)算诵冒,會(huì)給我生成尼瑪一堆訂單凯肋,而且支付寶無限重復(fù)喚起啊。汽馋。侮东。我內(nèi)心一開始是拒絕的圈盔,這尼瑪CTO估計(jì)是閑的,后來想想悄雅,這種閑的沒事干的用戶估計(jì)還挺多驱敲,然后聯(lián)想到這將近5、60個(gè)界面啊宽闲,我該咋辦众眨。后來網(wǎng)上一找,果然一大推方案啊容诬,其中最一勞永逸的方案即是利用我們偉大的runtime機(jī)制去實(shí)現(xiàn)一勞永逸的做法娩梨。我內(nèi)心一陣狂喜,按照方案做了一遍放案,果然姚建,大功告成,啥吱殉?你們要我貼方案暗г!其實(shí)網(wǎng)上一大堆的友雳,不過既然你們想要稿湿,那我就貼出來吧。多個(gè)入口押赊,省點(diǎn)時(shí)間饺藤,本著造福跟我一樣萌萌的程序媛,我就犧牲一下自己把流礁。
step1:創(chuàng)建個(gè)UIControl的分類
step2:利用runtime動(dòng)態(tài)的給分類綁定屬性(不會(huì)涕俗?不著急,一會(huì)就給出)
step3:關(guān)鍵:利用method_exchangeImplementations方法交換函數(shù)的實(shí)現(xiàn)(只能交換一次哦)神帅。
代碼如下:
#import "UIControl+HQStopMultiTap.h"
@implementation UIControl (HQStopMultiTap)
- (NSTimeInterval)timeInterval
{
return[objc_getAssociatedObject(self,_cmd)doubleValue];
}
- (void)setTimeInterval:(NSTimeInterval)timeInterval
{
objc_setAssociatedObject(self,@selector(timeInterval),@(timeInterval),OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
//runtime動(dòng)態(tài)綁定屬性
- (void)setIsIgnoreEvent:(BOOL)isIgnoreEvent{
objc_setAssociatedObject(self,@selector(isIgnoreEvent),@(isIgnoreEvent),OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (BOOL)isIgnoreEvent{
return[objc_getAssociatedObject(self,_cmd)boolValue];
}
- (void)resetState{
[self setIsIgnoreEvent:NO];
}
+ (void)load{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
SEL selA =@selector(sendAction:to:forEvent:);
SEL selB =@selector(mySendAction:to:forEvent:);
Method methodA =class_getInstanceMethod(self, selA);
Method methodB =class_getInstanceMethod(self, selB);
//將methodB的實(shí)現(xiàn)添加到系統(tǒng)方法中也就是說將methodA方法指針添加成方法methodB的再姑。返回值表示是否添加成功
BOOL isAdd =class_addMethod(self, selA,method_getImplementation(methodB),method_getTypeEncoding(methodB));
//添加成功了說明本類中不存在methodB所以此時(shí)必須將方法b的實(shí)現(xiàn)指針換成方法A的,否則b方法將沒有實(shí)現(xiàn)找御。
if(isAdd) {
class_replaceMethod(self, selB,method_getImplementation(methodA),method_getTypeEncoding(methodA));
}else{
//添加失敗了說明本類中有methodB的實(shí)現(xiàn)元镀,此時(shí)只需要將methodA和methodB的IMP互換一下即可。
method_exchangeImplementations(methodA, methodB);
}
});
}
- (void)mySendAction:(SEL)action to:(id)target forEvent:(UIEvent*)event
{
if([NSStringFromClass(self.class)isEqualToString:@"UIButton"]) {
self.timeInterval =self.timeInterval==0 ? 1.5:self.timeInterval;
if(self.isIgnoreEvent){
return;
}else if(self.timeInterval>0){
[self performSelector:@selector(resetState) withObject:nil afterDelay:self.timeInterval];
}
}
//此處methodA和methodB方法IMP互換了霎桅,實(shí)際上執(zhí)行sendAction栖疑;所以不會(huì)死循環(huán)
self.isIgnoreEvent=YES;
[self mySendAction:action to:target forEvent:event];
}
@end
分類代碼就貼到這里了√鲜唬可是遇革,小姐姐是這種只想修復(fù)bug的人么?當(dāng)然得知道他背后的故事,對不對澳淑。
首先這個(gè)思想絕對不是我想出來的比原,我只是看完別人貼的代碼以后,做了點(diǎn)自己的思考杠巡,歡迎各位小哥哥們來噴量窘,不過,請手下留情氢拥。
首先runtime動(dòng)態(tài)綁定屬性也沒什么好分析的蚌铜。主要也就是 objc_setAssociatedObject/objc_getAssociatedObject這兩個(gè)方法。
那這兩個(gè)屬性
timeInterval 代表多長時(shí)間內(nèi)不能被連續(xù)點(diǎn)擊
isIgnoreEvent代表某個(gè)button是否不需要這種機(jī)制嫩海,可以自己設(shè)置冬殃。
其實(shí)主要的思想還是交換方法的實(shí)現(xiàn)。
對于一個(gè)給定的事件叁怪,UIControl會(huì)調(diào)用sendAction:to:forEvent:方法审葬,那我們就可以寫一個(gè)自己的方法,讓用戶點(diǎn)擊button的時(shí)候其實(shí)執(zhí)行的是我們自己的方法奕谭,在我們自己的方法里面再去調(diào)用自己的方法(因?yàn)榉椒ㄕ{(diào)換了等同于去調(diào)用了系統(tǒng)的方法)涣觉,但是確保用戶下次點(diǎn)擊的時(shí)候不會(huì)再進(jìn)行調(diào)換,那尼瑪就瞎了血柳,又換回去了官册,搞毛啊。所以难捌,用
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
}
保證只會(huì)交換一次膝宁。OK?
哎根吁,最近小姐姐我也在研究runtime员淫,好高深的樣子,歡迎各位小哥哥來指教击敌。對了介返,還有什么問題也可以給我留言,我盡量正經(jīng)的回答你愚争。。挤聘。