(转)iOS中常见crash总结

1、找不到方法的实现unrecognized selector sent to instance

2、KVC造成的crash

3、EXC_BAD_ACCESS

4、KVO引起的崩溃

5、集合类相关崩溃

6、多线程中的崩溃

7、Socket长连接,进入后台没有关闭

8、Watch Dog超时造成的crash

9、后台返回NSNull导致的崩溃,多见于Java做后台服务器开发语言

1、找不到方法的实现unrecognized selector sent to instance

1.1、场景对应的Code

#import "UnrecognizedSelectorVC.h"/**

 代理协议

 */

@protocol UnrecognizedSelectorVCDelegate@optional

- (void)notImplementionFunc;

@end

/**

 测试控制器的代理对象

 */

@interface UnrecognizedSelectorVCObj : NSObject@property (nonatomic, strong) NSString *name;

@end

@implementation UnrecognizedSelectorVCObj

@end

/**

 测试控制器

 */

@interface UnrecognizedSelectorVC ()

@property(nonatomic, weak) iddelegate;

@property(nonatomic, copy) NSMutableArray *mutableArray;

@end

@implementation UnrecognizedSelectorVC

- (void)viewDidLoad {

    [super viewDidLoad];

    [self case1];

}

/**

 场景一:没有实现代理

 */

- (void)case1 {

    UnrecognizedSelectorVCObj* obj = [[UnrecognizedSelectorVCObj alloc] init];

    self.delegate = obj;

    // 崩溃:reason: '-[UnrecognizedSelectorVCObj notImplementionFunc]: unrecognized selector sent to instance 0x2808047f0'[self.delegate notImplementionFunc];

    // 解决办法:应该使用下面的代码if ( [self.delegate respondsToSelector:@selector(notImplementionFunc)] ) {

        [self.delegate notImplementionFunc];

    }

}

/**

 场景二:可变属性使用copy修饰

 */

- (void)case2 {

    NSMutableArray* array = [NSMutableArray arrayWithObjects:@1, @2, @3, nil];

    self.mutableArray = array;

    // 崩溃:reason: '-[__NSArrayI addObject:]: unrecognized selector sent to instance 0x281198a50'[self.mutableArray addObject:@4];

    // 原因:NSMutableArray经过copy之后变成NSArray

// @property (nonatomic, copy) NSMutableArray *mArray;

// 等同于

// - (void)setMArray:(NSMutableArray *)mArray {

//    _mArray = mArray.copy;

//}

    // 解决办法:使用strong修饰或者重写set方法

// 知识点:集合类对象和非集合类对象的copy与mutableCopy

// [NSArray copy]                  // 浅复制(新的和原来的是一个array)

// [NSArray mutableCopy]           // 深复制(array是新的,但是内容还是原来的,内容的指针没有变化)

// [NSMutableArray copy]           // 深复制(array是新的,但是内容还是原来的,内容的指针没有变化)

// [NSMutableArray mutableCopy]    // 深复制(array是新的,但是内容还是原来的,内容的指针没有变化)

}

/**

 场景三:低版本系统使用高版本API

 */

- (void)case3 {if (@available(iOS 10.0, *)) {

        [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {


        }];

    } else {

        // Fallback on earlier versions

    }

}

@end

1.2、原因

找不到方法iOS系统抛出异常崩溃

1.3、解决方案:

1、给NSObject添加一个分类,实现消息转发的几个方法

#import "NSObject+SelectorCrash.h"@implementation NSObject (SelectorCrash)

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {if ([self respondsToSelector:aSelector]) {

        // 已实现不做处理return [self methodSignatureForSelector:aSelector];

    }return [NSMethodSignature signatureWithObjCTypes:"v@:"];

}

- (void)forwardInvocation:(NSInvocation *)anInvocation {

    NSLog(@"在 %@ 类中, 调用了没有实现的实例方法: %@ ",NSStringFromClass([self class]),NSStringFromSelector(anInvocation.selector));

}

+ (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {if ([self respondsToSelector:aSelector]) {

        // 已实现不做处理return [self methodSignatureForSelector:aSelector];

    }return [NSMethodSignature signatureWithObjCTypes:"v@:"];

}

+ (void)forwardInvocation:(NSInvocation *)anInvocation {

    NSLog(@"在 %@ 类中, 调用了没有实现的类方法: %@ ",NSStringFromClass([self class]),NSStringFromSelector(anInvocation.selector));

}

2、尽量避免使用performSelector一系列方法

3、delegate 方法调用前进行 respondsToSelector 判断,或者Release模式下使用ProtocolKit给协议添加默认实现防止崩溃,Debug模式下关闭默认实现

4、属性和成员变量不要重名定义,合理使用 synthesize 生成属性的 setter 和 getter 方法

5、在MRC模式下,变量的 retain 和 release 要谨慎,建议采用安全 release 方法,即 release 的对象置为 nil

6、在.h中声明的方法如果用不到就去掉,用得到就同时在.m文件中实现

7、可变属性(如NSMutableArray),不要使用copy修饰,或者重写set方法

8、使用高版本的系统方法的时候做判断

1.4、知识归纳:参考runtime 消息转发

消息转发机制主要包含三个步骤

1、动态方法解析阶段

+(BOOL)resolveClassMethod:(SEL)sel或者+(BOOL)resolveInstanceMethod:(SEL)sel

2、备用接收者阶段

- (id)forwardingTargetForSelector:(SEL)aSelector

3、完整消息转发阶段

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector

- (void)forwardInvocation:(NSInvocation *)anInvocation

图片摘自:Objective-C 消息发送与转发机制原理

使用命令:thread backtrace查看线程堆栈

2、KVC造成的crash

2.1、场景对应的Code

#import "KvcCrashVC.h"@interface KvcCrashVCObj : NSObject

@property (nonatomic, strong) NSString *name;

@end

@implementation KvcCrashVCObj

- (void)setValue:(id)value forUndefinedKey:(NSString *)key {   

}

- (id)valueForUndefinedKey:(NSString *)key {return nil;

}

@end

@interface KvcCrashVC ()

@end

@implementation KvcCrashVC

- (void)viewDidLoad {

    [super viewDidLoad];

    [self case1];

}

/**

 场景一:对象不支持KVC

 */

- (void)case1 {

    // reason: '[setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key key.'NSObject* obj = [[NSObject alloc]init];

    [obj setValue:@"value" forKey:@"key"];

}

/**

 场景二:key为nil

 */

- (void)case2 {

    // reason: '*** -[KvcCrashVCObj setValue:forKey:]: attempt to set a value for a nil key'KvcCrashVCObj* obj = [[KvcCrashVCObj alloc]init];

    // value 为nil不会崩溃

    [obj setValue:nil forKey:@"name"];

    // key为nil会崩溃(直接写nil编译器会提示警告,更多时候我们传的是变量)

    [obj setValue:@"value" forKey:nil];

}

/**

 场景三:key不是object的属性产生的crash

 */

- (void)case3 {

    // reason: '[setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key falseKey.'KvcCrashVCObj* obj = [[KvcCrashVCObj alloc]init];

    [obj setValue:nil forKey:@"falseKey"];

}

@end

2.2、原因

给不存在的key(包括key为nil)设置value

[obj setValue:@"value" forKey:@"UndefinedKey"]

[obj valueForKey:@"UndefinedKey"]

2.3、场景:

2.4、解决方案:

1、如果属性存在,利用iOS的反射机制来规避,NSStringFromSelector(@selector())将SEL反射为字符串作为key。这样在@selector()中传入方法名的过程中,编译器会有合法性检查,如果方法不存在或未实现会报黄色警告。

2、重写类的setValue:forUndefinedKey:和valueForUndefinedKey:

-(void)setValue:(id)value forUndefinedKey:(NSString *)key{

}

-(id)valueForUndefinedKey:(NSString *)key{return nil;

}

2.5、知识归纳:参考runtime kvc原理分析

3、EXC_BAD_ACCESS

经过ARC的洗礼之后,普通的访问释放对象产生的EXC_BAD_ACCESS已经大量减少了,现在出现的EXC_BAD_ACCESS有很大一部分来自malloc的对象或者越界访问。

3.1、场景对应的Code

#import "BadAccessCrashVC.h"#import@interface BadAccessCrashVC (AssociatedObject)

@property (nonatomic, strong) UIView *associateView;

@end

@implementation BadAccessCrashVC (AssociatedObject)

- (void)setAssociateView:(UIView *)associateView {

    objc_setAssociatedObject(self, @selector(associateView), associateView, OBJC_ASSOCIATION_ASSIGN);

    //objc_setAssociatedObject(self, @selector(associateView), associateView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}

- (UIView *)associateView {return objc_getAssociatedObject(self, _cmd);;

}

@end

@interface BadAccessCrashVC ()

@property (nonatomic, copy)                         void(^blcok)(void);

@property (nonatomic, weak) UIView*                 weakView;

@property (nonatomic, unsafe_unretained) UIView*    unSafeView;

@property (nonatomic, assign) UIView*               assignView;

@end

@implementation BadAccessCrashVC

- (void)viewDidLoad {

    [super viewDidLoad];

    [self case1];

}

/**

 悬挂指针:访问没有实现的blcok

 */

- (void)case1 {

    self.blcok();

}

/**

 悬挂指针:对象没有被初始化

 */

- (void)case2 {

    UIView* view = [UIView alloc];

    view.backgroundColor = [UIColor blackColor];

    [self.view addSubview:view];

}

/**

 悬挂指针:访问的对象已经被释放掉

 */

- (void)case3 {

    {

        UIView* view = [[UIView alloc]init];

        view.backgroundColor = [UIColor blackColor];

        self.weakView = view;

        self.unSafeView = view;

        self.assignView = view;

        self.associateView = view;

    }

    // ARC下weak对象释放后会自动置nil,因此下面的代码不会崩溃

    [self.view addSubview:self.weakView];

    // 野指针场景一:unsafe_unretained修饰的对象释放后,不会自动置nil,变成野指针,因此下面的代码会崩溃

    [self.view addSubview:self.unSafeView];

    // 野指针场景二:应该使用strong/weak修饰的对象,却错误的使用assign修饰,释放后不会自动置nil

    [self.view addSubview:self.assignView];

    // 野指针场景三:给类添加添加关联变量的时候,类似场景二,应该使用OBJC_ASSOCIATION_RETAIN_NONATOMIC修饰,却错误使用OBJC_ASSOCIATION_ASSIGN

    [self.view addSubview:self.associateView];

}

@end

3.2、原因

出现悬挂指针,对象没有被初始化,或者访问的对象被释放

3.3、解决方案:

1、Debug阶段开启僵尸模式,Release时关闭僵尸模式

2、使用Xcode的Address Sanitizer检查地址访问越界

3、创建对象的时候记得初始化

4、对象的属性使用正确的修饰方式(strong/weak)

5、调用block的时候,做判断

4、KVO引起的崩溃

4.1、场景对应的Code

#import "KvoCrashVC.h"@interface KvoCrashVCObj : NSObject

@property (nonatomic, strong) NSString *name;

@end

@implementation KvoCrashVCObj

@end

@interface KvoCrashVC ()

@property (nonatomic, strong) KvoCrashVCObj *sObj;

@end

@implementation KvoCrashVC

- (void)viewDidLoad {

    [super viewDidLoad];

    self.sObj = [[KvoCrashVCObj alloc] init];

//#import//    static dispatch_once_t onceToken;

//    dispatch_once(&onceToken, ^{

//        [XXShieldSDK registerStabilityWithAbility:(EXXShieldTypeKVO)];

//    });

}

- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent *)event {

    [self func4];

}

/**

 观察者是局部变量,会崩溃

 */

- (void)func1 {

    // 崩溃日志:An -observeValueForKeyPath:ofObject:change:context: message was received but not handled.

    KvoCrashVCObj* obj = [[KvoCrashVCObj alloc] init];

    [self addObserver:obj           forKeyPath:@"view"  options:NSKeyValueObservingOptionNew

              context:nil];

    self.view = [[UIView alloc] init];

}

/**

 被观察者是局部变量,会崩溃

 */

- (void)func2 {

    // 崩溃日志:An -observeValueForKeyPath:ofObject:change:context: message was received but not handled.

    KvoCrashVCObj* obj = [[KvoCrashVCObj alloc] init];

    [obj addObserver:self          forKeyPath:@"name" options:NSKeyValueObservingOptionNew

             context:nil];

    obj.name = @"";

}

/**

 没有实现observeValueForKeyPath:ofObject:changecontext:方法:,会崩溃

 */

- (void)func3 {

    // 崩溃日志:An -observeValueForKeyPath:ofObject:change:context: message was received but not handled.

    [self.sObj addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:NULL];

    self.sObj.name = @"0";

}

/**

 重复移除观察者,会崩溃

 */

- (void)func4 {

    // 崩溃日志:because it is not registered as an observer

    [self.sObj addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:NULL];

    self.sObj.name = @"0";

    [self.sObj removeObserver:self forKeyPath:@"name"];

    [self.sObj removeObserver:self forKeyPath:@"name"];

}

/**

 重复添加观察者,不会崩溃,但是添加多少次,一次改变就会被观察多少次

 */

- (void)func5 {

    [self.sObj addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:NULL];

    self.sObj.name = @"0";

}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void *)context {

    NSLog(@"keyPath = %@", keyPath);

}

// 总结:KVO有两种崩溃

// 1、because it is not registered as an observer

// 2、An -observeValueForKeyPath:ofObject:change:context: message was received but not handled.

@end

4.2、原因

添加了观察者,没有在正确的时机移除

4.3、解决方案:

1、addObserver和removeObserver一定要成对出现,

2、推荐使用FaceBook开源的第三方库 FBKVOController

5、集合类相关崩溃

5.1、场景对应的Code

#import "CollectionCrashVC.h"@interface CollectionCrashVC ()

@end

@implementation CollectionCrashVC

- (void)viewDidLoad {

    [super viewDidLoad];

    [self case4];

}

/**

 场景一:数组越界

 */

- (void)case1 {

    // reason: '*** -[__NSArrayI objectAtIndex:]: index 4 beyond bounds [0 .. 2]'NSArray* array = [[NSArray alloc]initWithObjects:@1, @2, @3, nil];

    NSNumber* number = [array objectAtIndex:4];

    NSLog(@"number = %@", number);

}

/**

 场景二:向数组中添加nil元素

 */

- (void)case2 {

    // reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil'NSMutableArray* array = [[NSMutableArray alloc]initWithObjects:@1, @2, @3, nil];

    [array addObject:nil];

}

/**

 场景三:数组遍历的时候使用错误的方式移除元素

 */

- (void)case3 {

    NSMutableArray* array = [NSMutableArray array];

    [array addObject:@1];

    [array addObject:@2];

    [array addObject:@3];

    [array addObject:@4];

    // 不崩溃

    [array enumerateObjectsUsingBlock:^(NSNumber * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {if (obj.integerValue == 1) {

            [array removeObject:obj];

        }

    }];

    // 崩溃,reason: '*** Collectionwas mutated while being enumerated.'for (NSNumber* obj in array) {if (obj.integerValue == 2) {

            [array removeObject:obj];

        }

    }

    //    dispatch_async(dispatch_get_global_queue(0, 0), ^{

    //        self.view.backgroundColor = [UIColor blueColor];

    //    });

}

/**

 场景四:使用setObject:forKey:向字典中添加value为nil的键值对,推荐使用KVC的setValue:nil forKey:

 */

- (void)case4 {

    NSMutableDictionary* dictionary = [NSMutableDictionary dictionary];

    [dictionary setObject:@1 forKey:@1];

    // 不崩溃:value为nil,只会移除key对应的键值对

    [dictionary setValue:nil forKey:@1];

    // 崩溃:reason: '*** -[__NSDictionaryM setObject:forKey:]: object cannot be nil (key: 1)'[dictionary setObject:nil forKey:@1];

}

@end

5.2、原因

越界、添加nil、多线程非原子性操作、遍历的同时移除元素

5.3、场景:

1、数组越界,访问下标大于数组的个数

2、向数组中添加空数据

3、多线程环境中,一个线程在读取,一个线程在移除

4、一边遍历数组,一边移除数组中的元素

5、多线程中操作可变数组(数组的扩容、访问僵尸对象)

5.4、解决方案:

1、给集合类添加category重写原来的方法,在内部做判断

2、使用Runtime把原来的方法替换成自定义的安全方法

3、给NSMutableDictionary添加元素的时候,使用setObject:forKey:向字典中添加value为nil的键值对,推荐使用KVC的setValue:nil forKey:。[mutableDictionary setValue:nil ForKey:@"name"]不会崩溃,只是从字典中移除name键值对。

4、因为NSMutableArray、NSMutableDictionary不是线程安全的,所以在多线程环境下要保证读写操作的原子性,使用 加锁 、信号量 、GCD串行队列 、GCD栅栏dispatch_barrier_async、CGD组的dispatch_group_enter和dispatch_group_leave

6、多线程中的崩溃

6.1、场景对应的Code

#import "ThreadCrashVC.h"@interface ThreadCrashVC ()

@property (nonatomic, strong) NSMutableArray *array;

@end

@implementation ThreadCrashVC

- (void)viewDidLoad {

    [super viewDidLoad];

}

- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent *)event {

    [self case1];

}

/**

 dispatch_group_leave比dispatch_group_enter执行的次数多

 */

- (void)case1 {

    // 崩溃:Thread 1: EXC_BREAKPOINT (code=1, subcode=0x1054f6348)

    dispatch_group_t group = dispatch_group_create();

    dispatch_group_leave(group);

}

/**

 在子线程更新UI

 */

- (void)case2 {

    dispatch_async(dispatch_get_global_queue(0, 0), ^{

        self.view.backgroundColor = [UIColor redColor];

    });

}

/**

 多个线程同时释放一个对象

 */

- (void)case3 {   

    // ==================使用信号量同步后不崩溃==================

    {

        dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);

        __block NSObject *obj = [NSObject new];

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{while (YES) {

                dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

                obj = [NSObject new];

                dispatch_semaphore_signal(semaphore);

            }

        });while (YES) {

            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

            obj = [NSObject new];

            dispatch_semaphore_signal(semaphore);

        }

    }

    // ==================未同步则崩溃==================

    {

        __block NSObject *obj = [NSObject new];

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{while (YES) {

                obj = [NSObject new];

            }

        });while (YES) {

            obj = [NSObject new];

        }

    }

}

/**

 多线程中的数组扩容、浅复制

 扩容:数组的地址已经改变,报错was mutated while being enumerated

 浅复制:访问僵尸对象,报错EXC_BAD_ACCESS


 // 知识点:集合类对象和非集合类对象的copy与mutableCopy

 // [NSArray copy]                  // 浅复制

 // [NSArray mutableCopy]           // 深复制

 // [NSMutableArray copy]           // 深复制

 // [NSMutableArray mutableCopy]    // 深复制


 参考:

 [Swift数组扩容原理](https://bestswifter.com/swiftarrayappend/)

 [戴仓薯](https://juejin.im/post/5a9aa633518825556a71d9f3)

 */

-(void)case4 {

    {

        NSArray *array = @[@[@"a", @"b"], @[@"c", @"d"]];

        NSArray *copyArray = [array copy];

        NSMutableArray *mCopyArray = [array mutableCopy];

        NSLog(@"array = %p,copyArray = %p,mCopyArray = %p", array, copyArray, mCopyArray);

    }

    {

        NSMutableArray *array = [NSMutableArray arrayWithObjects:[NSMutableString stringWithString:@"a"],@"b",@"c",nil];

        NSArray *copyArray = [array copy];

        NSMutableArray *mCopyArray = [array mutableCopy];

        NSLog(@"array = %p,copyArray = %p,mCopyArray = %p", array, copyArray, mCopyArray);

    }


    dispatch_queue_t queue1 = dispatch_queue_create("queue1", 0);

    dispatch_queue_t queue2 = dispatch_queue_create("queue2", 0);


    NSMutableArray* array = [NSMutableArray array];


    dispatch_async(queue1, ^{while (true) {if (array.count < 10) {

                [array addObject:@(array.count)];

            } else {

                [array removeAllObjects];

            }

        }

    });


    dispatch_async(queue2, ^{while (true) {

            // case 1:数组扩容for (NSNumber* number in array) {

              NSLog(@"%@", number);

            }

            // case 2:数组扩容

            NSArray* immutableArray = array;for (NSNumber* number in immutableArray) {

              NSLog(@"%@", number);

            }

            // case 3:浅复制 在 [NSArray copy] 的过程,

            // copy 方法内部调用initWithArray:range:copyItems: 时

            // 数组被另一个线程清空,range 不一致导致抛出 exception

            NSArray* immutableArray1 = [array copy];for (NSNumber* number in immutableArray1) {

                NSLog(@"%@", number);

            }

            // case 4:浅复制 数组内的对象被其他线程释放,访问僵尸对象

            NSArray* immutableArray2 = [array mutableCopy];for (NSNumber* number in immutableArray2) {

                NSLog(@"%@", number);

            }

        }

    });

}

@end

6.2、原因

死锁、子线程中更新UI、多个线程同时释放一个对象

6.3、场景

1、在子线程中更新UI

2、dispatch_group crash,dispatch_group_leave的次数比dispatch_group_enter次数多。参考:iOS疑难问题排查之深入探究dispatch_group crash

3、多线程下非线程安全类的使用,如NSMutableArray、NSMutableDictionary。NSCache是线程安全的。

4、数据缓存到磁盘和读取。

6.4、解决方案:

多线程遇到需要同步的时候,加锁,添加信号量等进行同步操作。一般多线程发生的Crash,会收到SIGSEGV信号,表明试图访问未分配给自己的内存, 或试图往没有写权限的内存地址写数据。

7、Socket长连接,进入后台没有关闭

当服务器close一个连接时,若client端接着发数据。根据TCP协议的规定,会收到一个RST响应,client再往这个服务器发送数据时,系统会发出一个SIGPIPE信号给进程,告诉进程这个连接已经断开了,不要再写了。而根据信号的默认处理规则,SIGPIPE信号的默认执行动作是terminate(终止、退出),所以client会退出。

长连接socket或重定向管道进入后台,没有关闭导致崩溃的解决办法:

7.1、解决方案:

方法一:1、切换到后台是,关闭长连接和管道,回到前台重新创建。

方法二:2、使用signal(SIGPIPE,SIG_IGN),将SIGPIP交给系统处理,这么做将SIGPIPE设为SIG_IGN,使客户端不执行默认操作,即不退出。

8、Watch Dog超时造成的crash

主线程执行耗时操作,导致主线程被卡超过一定时间。一般异常编码是0x8badf00d,表示应用发生watch dog超时而被iOS终止,通常是应用花费太多的时间无法启动、终止或者响应系统事件。

8.1、解决方案:

主线程只负责更新UI和事件响应,将耗时操作(网络请求、数据库读写等)异步放到后台线程执行。

9、后台返回NSNull导致的崩溃,多见于Java做后台服务器开发语言

9.1、场景对应的Code

// reason: '-[NSNull integerValue]: unrecognized selector sent to instance 0x2098f99b0'NSNull *nullStr = [[NSNull alloc] init];NSMutableDictionary* dic = [NSMutableDictionary dictionary];

[dic setValue:nullStr forKey:@"key"];NSNumber* number = [dic valueForKey:@"key"];复制代码

NULL:用于普通类型,例如NSInteger

nil:用于OC对象(除了类这个对象),给nil对象发送消息不会crash

Nil:用于Class类型对象的赋值(类是元类的实例,也是对象)

NSNull:用于OC对象的站位,一般会作为集合中的占位元素,给NSNull对象发送消息会crash的,后台给我们返回的就是NSNull对象

9.2、解决方法

利用消息转发。参考:NullSafe。当我们给一个NSNull对象发送消息的话,可能会崩溃(null是有内存的),而发送给nil的话,是不会崩溃的。

10、在iOS中捕获异常信息

崩溃主要是由于 Mach 异常、Objective-C 异常(NSException)引起的,同时对于 Mach 异常,到了 BSD层会转换为对应的 Signal 信号,那么我们也可以通过捕获信号,来捕获 Crash 事件。针对 NSException 可以通过注册 NSUncaughtExceptionHandler 捕获异常信息。

/**

 一、OC异常处理函数

 @param exception exception

 */

static void uncaught_exception_handler (NSException *exception) {

    // 异常的堆栈信息

    NSArray *stackArray = [exception callStackSymbols];

    // 出现异常的原因

    NSString *reason = [exception reason];

    // 异常名称

    NSString *name = [exception name];

    NSString *exceptionInfo = [NSString stringWithFormat:@"Exception reason:%@\nException name:%@\nException stack:%@",

                               name,

                               reason,

                               stackArray];

    NSLog(@"%@", exceptionInfo);

    NSMutableArray *tmpArr = [NSMutableArray arrayWithArray:stackArray];

    [tmpArr insertObject:reason atIndex:0];

    //保存到本地  --  当然你可以在下次启动的时候,上传这个log[exceptionInfo writeToFile:[NSString stringWithFormat:@"%@/Documents/error.log",NSHomeDirectory()]

                    atomically:YES

                      encoding:NSUTF8StringEncoding

                         error:nil];


    /**

     *  把异常崩溃信息发送至开发者邮件

     */

    NSString *content = [NSString stringWithFormat:@"==异常错误报告==\nname:%@\nreason:\n%@\ncallStackSymbols:\n%@",

                         name,

                         reason,

                         [stackArray componentsJoinedByString:@"\n"]];

    NSMutableString *mailUrl = [NSMutableString string];

    [mailUrl appendString:@"mailto:test@qq.com"];

    [mailUrl appendString:@"?subject=程序异常崩溃,请配合发送异常报告,谢谢合作!"];

    [mailUrl appendFormat:@"&body=%@", content];

    // 打开地址

    NSCharacterSet *set = [NSCharacterSet URLHostAllowedCharacterSet];

    NSString *mailPath = [mailUrl stringByAddingPercentEncodingWithAllowedCharacters:set];

    //NSString *mailPath = [mailUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:mailPath]];

}

/**

 二、Unix标准的signal机制处理函数

 @param sig sig

 */

static void handleSignal( int sig ) {


}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    // OC层中未被捕获的异常,通过注册NSUncaughtExceptionHandler捕获异常信息

    NSSetUncaughtExceptionHandler(&uncaught_exception_handler);

    // 内存访问错误,重复释放等错误就无能为力了,因为这种错误它抛出的是Signal,所以必须要专门做Signal处理

    // OC中层不能转换的Mach异常,利用Unix标准的signal机制,注册SIGABRT, SIGBUS, SIGSEGV等信号发生时的处理函数。

    signal(SIGSEGV,handleSignal);return YES;

}

参考资料

大白健康系统--iOS APP运行时Crash自动修复系统

iOS中的crash防护(一)unrecognized selector sent to instance

小萝莉说Crash(一):Unrecognized selector sent to instance xxxx

iOS实录14:浅谈iOS Crash(一)

iOS崩溃异常全局捕获

聊聊dealloc

iOS崩溃crash大解析

GitHub开源实现:

XXShield

QYCrashProtector

JKCrashProtect

WOCrashProtector

AvoidCrash

NeverCrash

转自: http://www.cocoachina.com/articles/27227

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,067评论 1 32
  • __block和__weak修饰符的区别其实是挺明显的:1.__block不管是ARC还是MRC模式下都可以使用,...
    LZM轮回阅读 3,270评论 0 6
  • 1.设计模式是什么? 你知道哪些设计模式,并简要叙述? 设计模式是一种编码经验,就是用比较成熟的逻辑去处理某一种类...
    司马DE晴空阅读 1,269评论 0 7
  • 设计模式是什么? 你知道哪些设计模式,并简要叙述? 设计模式是一种编码经验,就是用比较成熟的逻辑去处理某一种类型的...
    卑微的戏子阅读 618评论 0 1
  • 再一次翻开大学最后一年的日记。在这个有雪的日子,沉入。然后心痛的无法呼吸,眼泪止不住的掉了下来。我以为我不会再想起...
    淡云一片阅读 217评论 0 0