美文网首页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