iOS編程中的常見奔潰匯總
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做后臺服務器開發(fā)語言
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是新的痹愚,但是內容還是原來的富岳,內容的指針沒有變化)
}
/**
場景三:低版本系統(tǒng)使用高版本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系統(tǒng)拋出異常崩潰
1.3拯腮、解決方案:
1窖式、給NSObject添加一個分類,實現消息轉發(fā)的幾個方法
#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吨拍、使用高版本的系統(tǒng)方法的時候做判斷
1.4褪猛、知識歸納:參考runtime 消息轉發(fā)
消息轉發(fā)機制主要包含三個步驟:
1、動態(tài)方法解析階段
+(BOOL)resolveClassMethod:(SEL)sel或者
+(BOOL)resolveInstanceMethod:(SEL)sel
2羹饰、備用接收者階段
- (id)forwardingTargetForSelector:(SEL)aSelector
3伊滋、完整消息轉發(fā)階段
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
和
- (void)forwardInvocation:(NSInvocation *)anInvocation
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的反射機制來規(guī)避乌妙,NSStringFromSelector(@selector())將SEL反射為字符串作為key使兔。這樣在@selector()中傳入方法名的過程中,編譯器會有合法性檢查冠胯,如果方法不存在或未實現會報黃色警告火诸。
2、重寫類的setValue:forUndefinedKey:和valueForUndefinedKey:
-(void)setValue:(id)value forUndefinedKey:(NSString *)key{
}
-(id)valueForUndefinedKey:(NSString *)key{return nil;
}
3荠察、EXC_BAD_ACCESS
經過ARC的洗禮之后置蜀,普通的訪問釋放對象產生的EXC_BAD_ACCESS已經大量減少了,現在出現的EXC_BAD_ACCESS有很大一部分來自malloc的對象或者越界訪問悉盆。
#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、創(chuàng)建對象的時候記得初始化
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赂毯、多線程環(huán)境中战虏,一個線程在讀取,一個線程在移除
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不是線程安全的档插,所以在多線程環(huán)境下要保證讀寫操作的原子性慢蜓,使用 加鎖 、信號量 郭膛、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執(zhí)行的次數多
*/
- (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涮俄、解決方案:
多線程遇到需要同步的時候蛉拙,加鎖,添加信號量等進行同步操作彻亲。一般多線程發(fā)生的Crash刘离,會收到SIGSEGV信號室叉,表明試圖訪問未分配給自己的內存, 或試圖往沒有寫權限的內存地址寫數據睹栖。
7硫惕、Socket長連接,進入后臺沒有關閉
當服務器close一個連接時野来,若client端接著發(fā)數據恼除。根據TCP協議的規(guī)定,會收到一個RST響應曼氛,client再往這個服務器發(fā)送數據時,系統(tǒng)會發(fā)出一個SIGPIPE信號給進程,告訴進程這個連接已經斷開了王财,不要再寫了舌剂。而根據信號的默認處理規(guī)則,SIGPIPE信號的默認執(zhí)行動作是terminate(終止聊浅、退出),所以client會退出餐抢。
長連接socket或重定向管道進入后臺,沒有關閉導致崩潰的解決辦法:
7.1低匙、解決方案:
方法一:1旷痕、切換到后臺是,關閉長連接和管道顽冶,回到前臺重新創(chuàng)建欺抗。
方法二:2、使用signal(SIGPIPE,SIG_IGN)强重,將SIGPIP交給系統(tǒng)處理绞呈,這么做將SIGPIPE設為SIG_IGN,使客戶端不執(zhí)行默認操作间景,即不退出佃声。
8、Watch Dog超時造成的crash
主線程執(zhí)行耗時操作拱燃,導致主線程被卡超過一定時間秉溉。一般異常編碼是0x8badf00d,表示應用發(fā)生watch dog超時而被iOS終止碗誉,通常是應用花費太多的時間無法啟動召嘶、終止或者響應系統(tǒng)事件。
8.1哮缺、解決方案:
主線程只負責更新UI和事件響應弄跌,將耗時操作(網絡請求、數據庫讀寫等)異步放到后臺線程執(zhí)行尝苇。
9铛只、后臺返回NSNull導致的崩潰埠胖,多見于Java做后臺服務器開發(fā)語言
9.1、場景對應的Code
NULL
:用于普通類型淳玩,例如NSInteger
nil
:用于OC對象(除了類這個對象),給nil對象發(fā)送消息不會crashNil
:用于Class類型對象的賦值(類是元類的實例直撤,也是對象)NSNull
:用于OC對象的站位,一般會作為集合中的占位元素蜕着,給NSNull對象發(fā)送消息會crash的谋竖,后臺給我們返回的就是NSNull對象
9.2、解決方法
利用消息轉發(fā)承匣。參考:NullSafe蓖乘。當我們給一個NSNull對象發(fā)送消息的話,可能會崩潰(null是有內存的)韧骗,而發(fā)送給nil的話嘉抒,是不會崩潰的。
10袍暴、在iOS中捕獲異常信息
崩潰主要是由于 Mach
異常些侍、Objective-C
異常(NSException
)引起的,同時對于 Mach
異常容诬,到了 BSD
層會轉換為對應的 Signal
信號娩梨,那么我們也可以通過捕獲信號,來捕獲 Crash
事件览徒。針對 NSException
可以通過注冊 NSUncaughtExceptionHandler
捕獲異常信息狈定。