美文网首页iOS
iOS runtime之数据、字典越界及button重复点击处理

iOS runtime之数据、字典越界及button重复点击处理

作者: nenhall | 来源:发表于2016-11-24 10:35 被阅读143次

    一、runtime之数据、字典越界

    方法交换

    Runtime解决数据越界及字典key或value为nil的情况,主要通过Runtime的方法交换实现,可以扩展一下NSObject分类:

    @implementation NSObject (FlyElephant)

    - (void)swizzleMethod:(SEL)originalSelector swizzledSelector:(SEL)swizzledSelector{

    Class class = [self class];

    Method originalMethod = class_getInstanceMethod(class, originalSelector);

    Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);

    //可能方法不在这个类中,可能在父类中,因此尝试添加方法实现

    BOOL didAddMethod = class_addMethod(class,

    originalSelector,

    method_getImplementation(swizzledMethod),

    method_getTypeEncoding(swizzledMethod));

    //尝试添加方法实现 成功

    if (didAddMethod) {

    class_replaceMethod(class,

    swizzledSelector,

    method_getImplementation(originalMethod),

    method_getTypeEncoding(originalMethod));

    } else {//尝试添加方法实现 失败,说明已经存在这个方法,则可以直接交换方法的实现

    method_exchangeImplementations(originalMethod, swizzledMethod);

    }

    }

    @end

    扩展NSArray和NSDictionary分类,实现方法交换:

    NSArray0表示一般空数组,NSArrayI表示一般数组,__NSArrayM可变数组,NSArray和NSMutableArray相当于工厂,最终实现通过上面三个类进行实现的,有兴趣的可以自行了解一下类簇

    @implementation NSArray (FlyElephant)

    + (void)load{

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

    @autoreleasepool {

    [objc_getClass("__NSArray0") swizzleMethod:@selector(objectAtIndex:) swizzledSelector:@selector(emptyObjectIndex:)];

    [objc_getClass("__NSArrayI") swizzleMethod:@selector(objectAtIndex:) swizzledSelector:@selector(arrObjectIndex:)];

    [objc_getClass("__NSArrayM") swizzleMethod:@selector(objectAtIndex:) swizzledSelector:@selector(mutableObjectIndex:)];

    [objc_getClass("__NSArrayM") swizzleMethod:@selector(insertObject:atIndex:) swizzledSelector:@selector(mutableInsertObject:atIndex:)];

    }

    });

    }

    - (id)emptyObjectIndex:(NSInteger)index{

    return nil;

    }

    - (id)arrObjectIndex:(NSInteger)index{

    if (index >= self.count || index < 0) {

    return nil;

    }

    return [self arrObjectIndex:index];

    }

    - (id)mutableObjectIndex:(NSInteger)index{

    if (index >= self.count || index < 0) {

    return nil;

    }

    return [self mutableObjectIndex:index];

    }

    - (void)mutableInsertObject:(id)object atIndex:(NSUInteger)index{

    if (object) {

    [self mutableInsertObject:object atIndex:index];

    }

    }

    @end

    @implementation NSDictionary (FlyElephant)

    + (void)load{

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

    @autoreleasepool {

    [objc_getClass("__NSDictionaryM") swizzleMethod:@selector(setObject:forKey:) swizzledSelector:@selector(mutableSetObject:forKey:)];

    }

    });

    }

    - (void)mutableSetObject:(id)obj forKey:(NSString *)key{

    if (obj && key) {

    [self mutableSetObject:obj forKey:key];

    }

    }

    @end

    通过Runtime进行对数组字典进行方法交换之后,之前对键盘输入的场景没有完全弄清楚,完整过程应该是,文本框输入文字→Home键进入后台→点击App图标重新进入→崩溃,有的时候前两步就直接崩溃了

    **[UIKeyboardLayoutStar release]: message sent to deallocated instance 0x1459e0600**

    因此Runtime的这个文件需要通过mrc管理:

    一般项目都是 ARC 模式,需要单独问Runtime的这个问题添加MRC支持,

    Build Phases -> Compile Sources找到文件设置 -fno-objc-arc 标签。

    如果项目是MRC 模式,则为 ARC 模式的代码文件加入 -fobjc-arc 标签.

    同时可以为相应的代码块添加autoreleasepool:

    @autoreleasepool{if(index >=self.count|| index <0) {returnnil;        }return[selfarrObjectIndex:index];    }

    引用链接1:http://www.jianshu.com/p/5492d2d3342b 

    引用链接2:http://www.cnblogs.com/chushenruhua/p/5667580.html

    二、避免重复恶意点击

    #import#define defaultInterval 0.5  //默认时间间隔@interface UIButton (Swizzling)@property (nonatomic, assign) NSTimeInterval timeInterval;@end#import "UIButton+Swizzling.h"#import#import "NSObject+Swizzling.h"

    @interface UIButton()

    /**bool 类型 YES 不允许点击  NO 允许点击  设置是否执行点UI方法*/

    @property (nonatomic, assign) BOOL isIgnoreEvent;

    @end

    @implementation UIButton (Swizzling)

    +(void)load

    {

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

    [objc_getClass("UIButton") swizzleSelector:@selector(sendAction:to:forEvent:) withSwizzledSelector:@selector(customSendAction:to:forEvent:)];

    });

    }

    - (void)customSendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event

    {

    if ([NSStringFromClass(self.class) isEqualToString:@"UIButton"]) {

    self.timeInterval =self.timeInterval ==0 ?defaultInterval:self.timeInterval;

    if (self.isIgnoreEvent){

    return;

    }else if (self.timeInterval > 0){

    [self performSelector:@selector(resetState) withObject:nil afterDelay:self.timeInterval];

    }

    }

    //此处 methodA和methodB方法IMP互换了,实际上执行 sendAction;所以不会死循环

    self.isIgnoreEvent = YES;

    [self customSendAction:action to:target forEvent:event];

    }

    - (NSTimeInterval)timeInterval

    {

    return [objc_getAssociatedObject(self, _cmd) doubleValue];

    }

    - (void)setTimeInterval:(NSTimeInterval)timeInterval

    {

    objc_setAssociatedObject(self, @selector(timeInterval), @(timeInterval), OBJC_ASSOCIATION_RETAIN_NONATOMIC);

    }

    //runtime 动态绑定 属性

    - (void)setIsIgnoreEvent:(BOOL)isIgnoreEvent{

    // 注意BOOL类型 需要用OBJC_ASSOCIATION_RETAIN_NONATOMIC 不要用错,否则set方法会赋值出错

    objc_setAssociatedObject(self, @selector(isIgnoreEvent), @(isIgnoreEvent), OBJC_ASSOCIATION_RETAIN_NONATOMIC);

    }

    - (BOOL)isIgnoreEvent{

    //_cmd == @select(isIgnore); 和set方法里一致

    return [objc_getAssociatedObject(self, _cmd) boolValue];

    }

    - (void)resetState{

    [self setIsIgnoreEvent:NO];

    }

    @end

    著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

    相关文章

      网友评论

        本文标题:iOS runtime之数据、字典越界及button重复点击处理

        本文链接:https://www.haomeiwen.com/subject/wxjbpttx.html