iOS-避免数组崩溃(Method Swizzling)

在项目开发过程中,经常因为NSArray数组越界或者value值为nil等问题导致的崩溃,
直接对NSArray进行Method Swizzling 是没用的,需要对对应的类进行操作才行。

数组是一个类簇从NSArray看类簇

有如下几种类型,对他们分别进行Method Swizzling之后能够一定避免由于数组越界导致的崩溃。

    NSArray *arr1 =  [NSArray alloc];
    NSArray *arr2 =  [[NSArray alloc]init];
    NSArray *arr3 =  @[@"1",@"2"];
    NSArray *arr4 =  [[NSArray alloc]initWithObjects:@"1",nil];
    NSArray *arr5 =  @[];
    NSLog(@"obj1:%@",[arr1 class]);
    NSLog(@"obj2:%@",[arr2 class]);
    NSLog(@"obj3:%@",[arr3 class]);
    NSLog(@"obj4:%@",[arr4 class]);
    NSLog(@"obj5:%@",[arr5 class]);

2018-01-30 14:20:04.272322 tesssss[1669:751109] obj1:__NSPlaceholderArray
2018-01-30 14:20:04.272409 tesssss[1669:751109] obj2:__NSArray0
2018-01-30 14:20:04.272441 tesssss[1669:751109] obj3:__NSArrayI
2018-01-30 14:20:04.272471 tesssss[1669:751109] obj4:__NSSingleObjectArrayI
2018-01-30 14:20:04.272497 tesssss[1669:751109] obj5:__NSArray0

NSArray类簇包含了__NSPlaceholderArray,__NSArray0,__NSArrayI这三种类型。

举一个类方法和静态方法例子

#import 

@interface NSArray (AvoidCrash)

+ (void)exchangeClassMethod:(Class)anClass
                 method1Sel:(SEL)method1Sel
                 method2Sel:(SEL)method2Sel ;

+ (void)exchangeInstanceMethod:(Class)anClass
                    method1Sel:(SEL)method1Sel
                    method2Sel:(SEL)method2Sel ;
@end

#import "NSArray+AvoidCrash.h"
#import 

@implementation NSArray (AvoidCrash)

/**
 *  类方法的交换
 *
 *  @param anClass    哪个类
 *  @param method1Sel 方法1
 *  @param method2Sel 方法2
 */
+ (void)exchangeClassMethod:(Class)anClass method1Sel:(SEL)method1Sel method2Sel:(SEL)method2Sel {
    Method method1 = class_getClassMethod(anClass, method1Sel);
    Method method2 = class_getClassMethod(anClass, method2Sel);
    method_exchangeImplementations(method1, method2);
}


/**
 *  对象方法的交换
 *
 *  @param anClass    哪个类
 *  @param method1Sel 方法1(原本的方法)
 *  @param method2Sel 方法2(要替换成的方法)
 */
+ (void)exchangeInstanceMethod:(Class)anClass method1Sel:(SEL)method1Sel method2Sel:(SEL)method2Sel {
    Method originalMethod = class_getInstanceMethod(anClass, method1Sel);
    Method swizzledMethod = class_getInstanceMethod(anClass, method2Sel);
    
    BOOL didAddMethod =
    class_addMethod(anClass,
                    method1Sel,
                    method_getImplementation(swizzledMethod),
                    method_getTypeEncoding(swizzledMethod));
    
    if (didAddMethod) {
        class_replaceMethod(anClass,
                            method2Sel,
                            method_getImplementation(originalMethod),
                            method_getTypeEncoding(originalMethod));
    }
    
    else {
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }
    
}

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [self exchangeClassMethod:[self class]
                       method1Sel:@selector(arrayWithObjects:count:)
                       method2Sel:@selector(p_ICEAvoidCrashArrayWithObjects:count:)];

        [self exchangeInstanceMethod:NSClassFromString(@"__NSArrayI")
                          method1Sel:@selector(objectsAtIndexes:)
                          method2Sel:@selector(p_ICEobjectsAtIndexes:)];
    });
}


#pragma mark - p_method
+ (instancetype)p_ICEAvoidCrashArrayWithObjects:(const id  _Nonnull __unsafe_unretained *)objects count:(NSUInteger)cnt {
    id instanceObject = nil;
    @try {
        instanceObject = [self p_ICEAvoidCrashArrayWithObjects:objects count:cnt];
    }
    @catch (NSException *exception) {
        // 自定义处理捕捉到的错误
        NSLog(@"%@", exception);
        
        // 尝试处理数组的问题
        NSInteger indexNew = 0;
        id  _Nonnull __unsafe_unretained arrNew[cnt];
        
        for (int i = 0; i < cnt; i++) {
            if (objects[i] != nil) {
                arrNew[indexNew] = objects[i];
                indexNew++;
            }
        }
        instanceObject = [self p_ICEAvoidCrashArrayWithObjects:arrNew count:indexNew];
    }
    @finally {
        return instanceObject;
    }
}

//__NSArrayI  objectAtIndex:
- (id)p_ICEobjectsAtIndexes:(NSUInteger)index {
    id object = nil;
    
    @try {
        object = [self p_ICEobjectsAtIndexes:index];
    }
    @catch (NSException *exception) {
        // 自定义处理捕捉到的错误
        NSLog(@"%@", exception);
    }
    @finally {
        return object;
    }
}

@end

iOS-runtime替换实现方法
Baymax:网易iOS App运行时Crash自动防护实践

你可能感兴趣的:(iOS-避免数组崩溃(Method Swizzling))