一、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
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。