方法交換(Method Swizzling),先來看看這兩張圖片瞭解一下方法的實現的步驟(圖片來源於網絡)。
一、方法未交前SEL與IMP是這樣的一一對應。
二、交換後SEL與IMP是這樣的對應關係。
三、具體代碼的實現是下面這樣。
+ (void)swizzleSelector:(SEL)originalSelector withSwizzledSelector:(SEL)swizzledSeletor{
Class class = [self class];
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzleMethod = class_getInstanceMethod(class, swizzledSeletor);
//若已經存在,則添加失敗
BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzleMethod), method_getTypeEncoding(swizzleMethod));
//若原來的方法並不存在,則添加即可
if (didAddMethod) {
class_replaceMethod(class, swizzledSeletor, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
}else{
method_exchangeImplementations(originalMethod, swizzleMethod);
}
}
四、方法交換的實際應用。——防止按鈕重複點擊。
(1)主要應用到了RunTime機制的動態添加屬性和方法交換。
(2)在UIButton的類別中.h文件添加屬性,timeInterval(表示第一次點擊幾秒之後再次點擊纔會響應)。
(3)動態添加屬性。
#pragma mark --- 動態添加屬性
- (NSTimeInterval)timeInterval{
return [objc_getAssociatedObject(self, ButtonTimeInterval) doubleValue];
}
- (void)setTimeInterval:(NSTimeInterval)timeInterval{
objc_setAssociatedObject(self, ButtonTimeInterval, @(timeInterval), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
(4)方法交換。
#pragma mark --- load方法內進行方法互換(這個方法只會在初始化被調用一次)
+ (void)load{
[self swizzleSelector:@selector(sendAction:to:forEvent:) withSwizzledSelector:@selector(custom_sendAction:to:forEvent:)];
}
(5)在自定義的要交換的方法中實現想要的效果。
#pragma mark --- 自定義方法的實現
- (void)custom_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event{
if (self.pass)return;
if (self.timeInterval > 0) {
self.pass = YES;
[self performSelector:@selector(resetPassValue) withObject:nil afterDelay:self.timeInterval];
}
[self custom_sendAction:action to:target forEvent:event];
}
- (void)resetPassValue{
[self setPass:NO];
}
(6)聰明的你肯定是知道爲什麼最後自定的方法要實現自己調用自己呢?這纔是方法交換的本質嘛,調用自己其實就是在調用系統的實現方法。