zoukankan      html  css  js  c++  java
  • 利用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
  • 相关阅读:
    C# delegate委托的用法
    C# new关键字的使用
    C# abstract抽象类的使用
    C# override关键字的使用
    C# sealed关键字的使用
    C# 虚函数virtual的使用
    Java IO流简介
    SpringBoot中异步请求的使用
    SpringBoot中异步调用的使用
    github
  • 原文地址:https://www.cnblogs.com/chushenruhua/p/5667580.html
Copyright © 2011-2022 走看看