美文网首页iOS DeveloperiOS点点滴滴
iOS-避免数组崩溃(Method Swizzling)

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

作者: linbj | 来源:发表于2018-01-30 16:14 被阅读60次

    在项目开发过程中,经常因为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 <Foundation/Foundation.h>
    
    @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 <objc/runtime.h>
    
    @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)

        本文链接:https://www.haomeiwen.com/subject/fnsqzxtx.html