方法交换(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)聪明的你肯定是知道为什么最后自定的方法要实现自己调用自己呢?这才是方法交换的本质嘛,调用自己其实就是在调用系统的实现方法。