runtime常见用法二(交换方法)

原理:

方法交换发生在运行时,当执行到交换代码的时候,SEL A 和SEL B的方法是实现就会被交换(交换的不是方法名).

实现思路:

交换SEL A 和SEL B

  • 给需要实现方法交换的类提供一个分类
  • 实现SEL B
  • 在分类的+load方法里实现方法的交换

实战举例:

防止按钮在短时间内被重复点击

#import 

NS_ASSUME_NONNULL_BEGIN

@interface UIControl (Limit)
@property (nonatomic, assign) float limitEventInterval;
@end

NS_ASSUME_NONNULL_END
#import "UIControl+Limit.h"
#import 
static const char *UIControl_limitEventInterval="UIControl_limitEventInterval";
static const char *UIControl_ignoreEvent="UIControl_ignoreEvent";
@implementation UIControl (Limit)
+(void)load {
    //a方法有可能是在父类中实现的,要做容错
    Method a = class_getInstanceMethod(self,@selector(sendAction:to:forEvent:));
    Method b = class_getInstanceMethod(self,@selector(swizzled_sendAction:to:forEvent:));
    BOOL didAddMethod = class_addMethod([self class],@selector(sendAction:to:forEvent:), method_getImplementation(b), method_getTypeEncoding(b));
    
    if (didAddMethod) {
        //a方法在父类中,要使用以下方法交换
        class_replaceMethod([self class], @selector(swizzled_sendAction:to:forEvent:), method_getImplementation(a), method_getTypeEncoding(a));
    }else{
        method_exchangeImplementations(a, b);
    }
}

- (void)setLimitEventInterval:(float)limitEventInterval{
    objc_setAssociatedObject(self,UIControl_limitEventInterval, @(limitEventInterval), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (float)limitEventInterval{
    return [objc_getAssociatedObject(self,UIControl_limitEventInterval) floatValue];
}

-(void)setIgnoreEvent:(BOOL)ignoreEvent{
    objc_setAssociatedObject(self,UIControl_ignoreEvent, @(ignoreEvent), OBJC_ASSOCIATION_ASSIGN);
}

-(BOOL)ignoreEvent{
    return [objc_getAssociatedObject(self,UIControl_ignoreEvent) boolValue];
}

- (void)swizzled_sendAction:(SEL)action to:(id)target forEvent:(UIEvent*)event
{
    if(self.ignoreEvent){
        NSLog(@"重复点击:%@",self);
        return;}
    if(self.limitEventInterval>0){
        self.ignoreEvent=YES;
        [self performSelector:@selector(setIgnoreEventWithNo)  withObject:nil afterDelay:self.limitEventInterval];
    }
    [self swizzled_sendAction:action to:target forEvent:event];
}

-(void)setIgnoreEventWithNo{
    self.ignoreEvent=NO;
}

@end

UIControl-Limit

注意点

1.交换方法的实现要写在load方法里面,因为交换方法只需要实现一次,如果执行了第二次,那这两个方法就会被交换回来了.必要时可以使用dispatch_once确保交换代码只执行一次
2.被交换的方法SEL A有可能是在父类中被实现的,此时需要执行

class_addMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp, 
                const char * _Nullable types) 

确保当前类有方法SEL A.

你可能感兴趣的:(runtime常见用法二(交换方法))