一、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.lxweimin.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
著作權歸作者所有,轉載請聯系作者獲得授權,并標注“簡書作者”。