利用runtime,避免UIButton 重复点击, 可变数组和可变字典为nil,或者数组越界导致的崩溃

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

 

posted @ 2016-07-13 17:04  出神入化VV  阅读(1081)  评论(0编辑  收藏  举报