标签:
Demo链接: https://github.com/ShaoWenLe/Runtimer-Demo.git
参考文章: http://www.jianshu.com/p/080a238c62b9
相关Runtime介绍: http://www.cocoachina.com/ios/20160523/16386.html
http://www.cocoachina.com/ios/20160628/16843.html
1 #import <Foundation/Foundation.h> 2 3 @interface NSObject (Swizzling) 4 + (void)swizzleSelector:(SEL)originalSelector withSwizzledSelector:(SEL)swizzledSelector; 5 @end 6 7 #import "NSObject+Swizzling.h" 8 #import <objc/runtime.h> 9 10 @implementation NSObject (Swizzling) 11 + (void)swizzleSelector:(SEL)originalSelector withSwizzledSelector:(SEL)swizzledSelector 12 { 13 Class class = [self class]; 14 15 Method originalMethod = class_getInstanceMethod(class, originalSelector); 16 17 Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); 18 19 //可能方法不在这个类中,可能在父类中,因此尝试添加方法实现 20 BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)); 21 //尝试添加方法实现 成功 22 if (didAddMethod) { 23 24 class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); 25 26 } else {//尝试添加方法实现 失败,说明已经存在这个方法,则可以直接交换方法的实现 27 28 method_exchangeImplementations(originalMethod, swizzledMethod); 29 30 } 31 32 33 } 34 @end 35 36 可变数组分类 37 #import <Foundation/Foundation.h> 38 39 @interface NSMutableArray (Swizzling) 40 41 @end 42 43 44 45 46 #import "NSMutableArray+Swizzling.h" 47 #import <objc/runtime.h> 48 #import "NSObject+Swizzling.h" 49 50 @implementation NSMutableArray (Swizzling) 51 52 + (void)load 53 { 54 55 static dispatch_once_t onceToken; 56 dispatch_once(&onceToken, ^{ 57 // [self swizzleSelector:@selector(removeObject:)withSwizzledSelector:@selector(safeRemoveObject:)]; 58 [objc_getClass("__NSArrayM") swizzleSelector:@selector(addObject:) withSwizzledSelector:@selector(safeAddObject:)]; 59 [objc_getClass("__NSArrayM") swizzleSelector:@selector(removeObjectAtIndex:) withSwizzledSelector:@selector(safeRemoveObjectAtIndex:)]; 60 [objc_getClass("__NSArrayM") swizzleSelector:@selector(insertObject:atIndex:) withSwizzledSelector:@selector(safeInsertObject:atIndex:)]; 61 [objc_getClass("__NSPlaceholderArray") swizzleSelector:@selector(initWithObjects:count:) withSwizzledSelector:@selector(safeInitWithObjects:count:)]; 62 [objc_getClass("__NSArrayM") swizzleSelector:@selector(objectAtIndex:) withSwizzledSelector:@selector(safeObjectAtIndex:)]; 63 }); 64 } 65 66 67 - (instancetype)safeInitWithObjects:(const id _Nonnull __unsafe_unretained *)objects count:(NSUInteger)cnt 68 { 69 BOOL hasNilObject = NO; 70 for (NSUInteger i = 0; i < cnt; i++) { 71 if ([objects[i] isKindOfClass:[NSArray class]]) { 72 // NSLog(@"%@", objects[i]); 73 } 74 if (objects[i] == nil || [objects[i] isEqual:[NSNull null]]) { 75 hasNilObject = YES; 76 // NSLog(@"%s object at index %lu is nil, it will be filtered", __FUNCTION__, i); 77 78 #if DEBUG 79 // 如果可以对数组中为nil的元素信息打印出来,增加更容 易读懂的日志信息,这对于我们改bug就好定位多了 80 NSString *errorMsg = [NSString stringWithFormat:@"数组元素不能为nil,其index为: %lu", i]; 81 // NSAssert(objects[i] != nil, errorMsg); 82 NSLog(@"%@", errorMsg); 83 #endif 84 } 85 } 86 87 // 因为有值为nil的元素,那么我们可以过滤掉值为nil的元素 88 if (hasNilObject) { 89 id __unsafe_unretained newObjects[cnt]; 90 NSUInteger index = 0; 91 for (NSUInteger i = 0; i < cnt; ++i) { 92 if (objects[i] != nil && ![objects[i] isEqual:[NSNull null]]) { 93 newObjects[index++] = objects[i]; 94 } 95 } 96 return [self safeInitWithObjects:newObjects count:index]; 97 } 98 return [self safeInitWithObjects:objects count:cnt]; 99 } 100 101 - (void)safeAddObject:(id)obj { 102 if (obj == nil || [obj isEqual:[NSNull null]]) { 103 #if DEBUG 104 NSLog(@"%s can add nil object into NSMutableArray", __FUNCTION__); 105 #endif 106 } else { 107 [self safeAddObject:obj]; 108 } 109 } 110 - (void)safeRemoveObject:(id)obj { 111 if (obj == nil || [obj isEqual:[NSNull null]]) { 112 #if DEBUG 113 NSLog(@"%s call -removeObject:, but argument obj is nil", __FUNCTION__); 114 #endif 115 return; 116 } 117 [self safeRemoveObject:obj]; 118 } 119 120 - (void)safeInsertObject:(id)anObject atIndex:(NSUInteger)index { 121 if (anObject == nil || [anObject isEqual:[NSNull null]]) { 122 #if DEBUG 123 NSLog(@"%s can‘t insert nil into NSMutableArray", __FUNCTION__); 124 #endif 125 } else if (index > self.count) { 126 #if DEBUG 127 NSLog(@"%s index is invalid", __FUNCTION__); 128 #endif 129 } else { 130 [self safeInsertObject:anObject atIndex:index]; 131 } 132 } 133 134 - (id)safeObjectAtIndex:(NSUInteger)index { 135 if (self.count == 0) { 136 #if DEBUG 137 NSLog(@"%s can‘t get any object from an empty array", __FUNCTION__); 138 #endif 139 return nil; 140 } 141 if (index > self.count) { 142 #if DEBUG 143 NSLog(@"%s index out of bounds in array", __FUNCTION__); 144 #endif 145 return nil; 146 } 147 return [self safeObjectAtIndex:index]; 148 } 149 150 - (void)safeRemoveObjectAtIndex:(NSUInteger)index { 151 if (self.count <= 0) { 152 #if DEBUG 153 NSLog(@"%s can‘t get any object from an empty array", __FUNCTION__); 154 #endif 155 return; 156 } 157 if (index >= self.count) { 158 #if DEBUG 159 NSLog(@"%s index out of bound", __FUNCTION__); 160 #endif 161 return; 162 } 163 [self safeRemoveObjectAtIndex:index]; 164 } 165 166 @end 167 168 可变字典分类 169 170 #import <Foundation/Foundation.h> 171 172 @interface NSMutableDictionary (Swizzling) 173 174 @end 175 176 177 178 #import "NSMutableDictionary+Swizzling.h" 179 #import <objc/runtime.h> 180 #import "NSObject+Swizzling.h" 181 182 @implementation NSMutableDictionary (Swizzling) 183 184 +(void)load 185 { 186 static dispatch_once_t onceToken; 187 dispatch_once(&onceToken, ^{ 188 189 [objc_getClass("__NSDictionaryM") swizzleSelector:@selector(setValue:forKey:) withSwizzledSelector:@selector(safeSetValue:forKey:)]; 190 [objc_getClass("__NSDictionaryM") swizzleSelector:@selector(setObject:forKey:) withSwizzledSelector:@selector(safeSetObject:forKey:)]; 191 [objc_getClass("__NSDictionaryM") swizzleSelector:@selector(removeObjectForKey:) withSwizzledSelector:@selector(safeRemoveObjectForKey:)]; 192 193 }); 194 } 195 - (void)safeSetValue:(id)value forKey:(NSString *)key 196 { 197 if (key == nil || value == nil || [key isEqual:[NSNull null]] || [value isEqual:[NSNull null]]) { 198 #if DEBUG 199 NSLog(@"%s call -safeSetValue:forKey:, key或vale为nil或null", __FUNCTION__); 200 #endif 201 return; 202 } 203 204 [self safeSetValue:value forKey:key]; 205 } 206 207 - (void)safeSetObject:(id)anObject forKey:(id<NSCopying>)aKey 208 { 209 if (aKey == nil || anObject == nil || [anObject isEqual:[NSNull null]]) { 210 #if DEBUG 211 NSLog(@"%s call -safeSetObject:forKey:, key或vale为nil或null", __FUNCTION__); 212 #endif 213 return; 214 } 215 216 [self safeSetObject:anObject forKey:aKey]; 217 } 218 219 - (void)safeRemoveObjectForKey:(id)aKey 220 { 221 if (aKey == nil || [aKey isEqual:[NSNull null]] ) { 222 #if DEBUG 223 NSLog(@"%s call -safeRemoveObjectForKey:, aKey为nil或null", __FUNCTION__); 224 #endif 225 return; 226 } 227 [self safeRemoveObjectForKey:aKey]; 228 } 229 230 231 @end 232 233 234 235 uibutton避免重复恶意点击 236 237 #import <UIKit/UIKit.h> 238 239 #define defaultInterval 0.5 //默认时间间隔 240 241 @interface UIButton (Swizzling) 242 @property (nonatomic, assign) NSTimeInterval timeInterval; 243 @end 244 245 246 247 248 #import "UIButton+Swizzling.h" 249 #import <objc/runtime.h> 250 #import "NSObject+Swizzling.h" 251 252 @interface UIButton() 253 /**bool 类型 YES 不允许点击 NO 允许点击 设置是否执行点UI方法*/ 254 @property (nonatomic, assign) BOOL isIgnoreEvent; 255 @end 256 @implementation UIButton (Swizzling) 257 258 +(void)load 259 { 260 static dispatch_once_t onceToken; 261 dispatch_once(&onceToken, ^{ 262 263 [objc_getClass("UIButton") swizzleSelector:@selector(sendAction:to:forEvent:) withSwizzledSelector:@selector(customSendAction:to:forEvent:)]; 264 265 }); 266 } 267 268 - (void)customSendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event 269 { 270 271 if ([NSStringFromClass(self.class) isEqualToString:@"UIButton"]) { 272 273 self.timeInterval =self.timeInterval ==0 ?defaultInterval:self.timeInterval; 274 if (self.isIgnoreEvent){ 275 return; 276 }else if (self.timeInterval > 0){ 277 [self performSelector:@selector(resetState) withObject:nil afterDelay:self.timeInterval]; 278 } 279 } 280 //此处 methodA和methodB方法IMP互换了,实际上执行 sendAction;所以不会死循环 281 self.isIgnoreEvent = YES; 282 [self customSendAction:action to:target forEvent:event]; 283 } 284 285 - (NSTimeInterval)timeInterval 286 { 287 return [objc_getAssociatedObject(self, _cmd) doubleValue]; 288 } 289 - (void)setTimeInterval:(NSTimeInterval)timeInterval 290 { 291 objc_setAssociatedObject(self, @selector(timeInterval), @(timeInterval), OBJC_ASSOCIATION_RETAIN_NONATOMIC); 292 293 } 294 //runtime 动态绑定 属性 295 - (void)setIsIgnoreEvent:(BOOL)isIgnoreEvent{ 296 // 注意BOOL类型 需要用OBJC_ASSOCIATION_RETAIN_NONATOMIC 不要用错,否则set方法会赋值出错 297 objc_setAssociatedObject(self, @selector(isIgnoreEvent), @(isIgnoreEvent), OBJC_ASSOCIATION_RETAIN_NONATOMIC); 298 } 299 - (BOOL)isIgnoreEvent{ 300 //_cmd == @select(isIgnore); 和set方法里一致 301 return [objc_getAssociatedObject(self, _cmd) boolValue]; 302 } 303 - (void)resetState{ 304 [self setIsIgnoreEvent:NO]; 305 } 306 @end
利用runtime,避免UIButton 重复点击, 可变数组和可变字典为nil,或者数组越界导致的崩溃
标签:
原文地址:http://www.cnblogs.com/chushenruhua/p/5667580.html