继承关系中慎用methodswizzle

记录一次在继承关系中遇到methodswizzle坑的经历:

1、在Category中使用runtime的methodswizzle统一为UIButton添加点击延迟的功能:

使用对象关联为UIButton添加延时间隔(cs_acceptEventInterval)和事件触发时间(cs_acceptEventTime)两个运行时的关联属性,

使用methodswizzle用@selector(cs_sendAction:to:forEvent:)替换@selector(sendAction:to:forEvent:),并在@selector(cs_sendAction:to:forEvent:)使用了上述的两个关联属性

2、问题:

UIButton使用一切正常,但是这个category却影响到了UIControl这个控件了,点击UIControl后,会收到

'-[*** cs_acceptEventTime]: unrecognized selector sent to instance 0x10520ce60'

的奔溃信息。

3、原因:

3.1、关联对象添加的两个关联属性作用的是UIButton这个类

3.2、虽然在UIButton的扩展中替换了@selector(sendAction:to:forEvent:),但这个方法是从UIControl中继承过来的,methodswizzle实际上是作用在底层结构体执行的IMP实现,本质上是改了UIControl的@selector(sendAction:to:forEvent:),这种,当UIControl触发事件的时候,运行的是@selector(cs_sendAction:to:forEvent:)这个Selctor的IMP实现,但是UIControl又并没有两个时间属性的对象关联属性,所以这时候就报错了。


4、在UIControl扩展中添加延迟时间的功能,以下代码能正常运行:

#import

@interfaceUIControl(FixMultiClick)

@property (nonatomic, assign) NSTimeInterval cs_acceptEventInterval; // 重复点击的间隔

@property (nonatomic, assign) NSTimeInterval cs_acceptEventTime;

@end



#import "UIControl+FixMultiClick.h"

#import 

@implementationUIControl(FixMultiClick)

static const char *UIControl_acceptEventInterval = "UIControl_acceptEventInterval";

- (NSTimeInterval)cs_acceptEventInterval {

    return  [objc_getAssociatedObject(self, UIControl_acceptEventInterval) doubleValue];

}

- (void)setCs_acceptEventInterval:(NSTimeInterval)cs_acceptEventInterval {

    objc_setAssociatedObject(self, UIControl_acceptEventInterval, @(cs_acceptEventInterval), OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}

static const char *UIControl_acceptEventTime = "UIControl_acceptEventTime";

- (NSTimeInterval)cs_acceptEventTime {

    return  [objc_getAssociatedObject(self, UIControl_acceptEventTime) doubleValue];

}

- (void)setCs_acceptEventTime:(NSTimeInterval)cs_acceptEventTime {

    objc_setAssociatedObject(self, UIControl_acceptEventTime, @(cs_acceptEventTime), OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}

// 在load时执行hook

+ (void)load{

    Methodbefore  =class_getInstanceMethod(self,@selector(sendAction:to:forEvent:));

    Methodafter    =class_getInstanceMethod(self,@selector(cs_sendAction:to:forEvent:));

    method_exchangeImplementations(before, after);

}

- (void)cs_sendAction:(SEL)actionto:(id)targetforEvent:(UIEvent*)event {

    if ([NSDate date].timeIntervalSince1970 - self.cs_acceptEventTime < self.cs_acceptEventInterval) {

        DLog(@"___间隔时间太短");

        return;

    }

    if (self.cs_acceptEventInterval > 0) {

        self.cs_acceptEventTime = [NSDate date].timeIntervalSince1970;

    }

    [selfcs_sendAction:actionto:targetforEvent:event];

}

@end


你可能感兴趣的:(继承关系中慎用methodswizzle)