iOS 中常見 Crash 總結

2021-02-20 Cocoa開發者社區

作者 | 在路上重名了啊

@(iOS總結)[溫故而知新] [TOC]

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 <NSObject>
@optional
- (void)notImplementionFunc;
@end

@interface UnrecognizedSelectorVCObj : NSObject<UnrecognizedSelectorVCDelegate>
@property (nonatomic, strong) NSString *name;
@end
@implementation UnrecognizedSelectorVCObj
@end

@interface UnrecognizedSelectorVC ()
@property(nonatomic, weak) id<UnrecognizedSelectorVCDelegate> delegate;
@property(nonatomic, copy) NSMutableArray *mutableArray;
@end
@implementation UnrecognizedSelectorVC
- (void)viewDidLoad {
    [super viewDidLoad];
    [self case1];
}

- (void)case1 {
    UnrecognizedSelectorVCObj* obj = [[UnrecognizedSelectorVCObj alloc] init];
    self.delegate = obj;
    
    [self.delegate notImplementionFunc];
    
    if ( [self.delegate respondsToSelector:@selector(notImplementionFunc)] ) {
        [self.delegate notImplementionFunc];
    }
}

- (void)case2 {
    NSMutableArray* array = [NSMutableArray arrayWithObjects:@1, @2, @3, nil];
    self.mutableArray = array;
    
    [self.mutableArray addObject:@4];
    
    
    
    
    
    
    

    
    
    
    
    
}

- (void)case3 {
    if (@available(iOS 10.0, *)) {
        [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {

        }];
    } else {
        
    }
}
@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];
}

- (void)case1 {
    
    NSObject* obj = [[NSObject alloc]init];
    [obj setValue:@"value" forKey:@"key"];
}

- (void)case2 {
    
    KvcCrashVCObj* obj = [[KvcCrashVCObj alloc]init];
    
    [obj setValue:nil forKey:@"name"];
    
    [obj setValue:@"value" forKey:nil];
}

- (void)case3 {
    
    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 <objc/runtime.h>
@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);
    
}
- (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];
}

- (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;
    }
    
    [self.view addSubview:self.weakView];
    
    [self.view addSubview:self.unSafeView];
    
    [self.view addSubview:self.assignView];
    
    [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];





}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [self func4];
}

- (void)func1 {
    
    KvoCrashVCObj* obj = [[KvoCrashVCObj alloc] init];
    [self addObserver:obj
           forKeyPath:@"view"
              options:NSKeyValueObservingOptionNew
              context:nil];
    self.view = [[UIView alloc] init];
}

- (void)func2 {
    
    KvoCrashVCObj* obj = [[KvoCrashVCObj alloc] init];
    [obj addObserver:self
          forKeyPath:@"name"
             options:NSKeyValueObservingOptionNew
             context:nil];
    obj.name = @"";
}

- (void)func3 {
    
    [self.sObj addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:NULL];
    self.sObj.name = @"0";
}

- (void)func4 {
    
    [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<NSKeyValueChangeKey,id> *)change context:(void *)context {
    NSLog(@"keyPath = %@", keyPath);
}



@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 {
    
    NSArray* array = [[NSArray alloc]initWithObjects:@1, @2, @3, nil];
    NSNumber* number = [array objectAtIndex:4];
    NSLog(@"number = %@", number);
}

- (void)case2 {
    
    NSMutableArray* array = [[NSMutableArray alloc]initWithObjects:@1, @2, @3, nil];
    [array addObject:nil];
}

- (void)case3 {
    NSMutableArray<NSNumber*>* 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];
        }
    }];
    
    for (NSNumber* obj in array) {
        if (obj.integerValue == 2) {
            [array removeObject:obj];
        }
    }
    
    
    
}

- (void)case4 {
    NSMutableDictionary* dictionary = [NSMutableDictionary dictionary];
    [dictionary setObject:@1 forKey:@1];
    
    [dictionary setValue:nil forKey:@1];
    
    [dictionary setObject:nil forKey:@1];
}
@end


5.2、原因

越界、添加nil、多線程非原子性操作、遍歷的同時移除元素


5.3、場景:
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<UITouch *> *)touches withEvent:(UIEvent *)event {
    [self case1];
}

- (void)case1 {
    
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_leave(group);
}

- (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];
        }
    }
}

-(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) {
            
            for (NSNumber* number in array) {
              NSLog(@"%@", number);
            }
            
            NSArray* immutableArray = array;
            for (NSNumber* number in immutableArray) {
              NSLog(@"%@", number);
            }
            
            
            
            NSArray* immutableArray1 = [array copy];
            for (NSNumber* number in immutableArray1) {
                NSLog(@"%@", number);
            }
            
            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



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 捕獲異常信息。


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];
    
    [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];
    
    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:mailPath]];
}

static void handleSignal( int sig ) {

}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    NSSetUncaughtExceptionHandler(&uncaught_exception_handler);
    
    
    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開源實現:

< END >

過去一個月最火的 10 個 Swift 開源項目

SwiftUI 的 DSL 語法分析

基於 JS 的高性能 Flutter 動態化框架 MXFlutter

相關焦點

  • iOS Crash 殺手排名
    從崩潰日誌記錄中,查詢到該問題的崩潰記錄有33條(總崩潰記錄304條),佔10.85%,崩潰率比較高。為什麼會出現這種現象呢?如何解決這樣的crash呢?崩潰率高的原因是因為自己的框架中採用了去model化的設計思想,不會把後臺返回的數據轉換成model,而是通過一個reformer機制轉換成NSDictionary形式,提供給目標對象使用,在轉換成NSDictionary的過程中,後臺返回的數據有時可能為空,就會造成插入nil對象,從而導致crash。
  • 再談 iOS App Crash 防護
    去年,網易杭州研究院曾經針對 crash 的防護有提出『大白健康系統--iOS APP 運行時 Crash 自動修復系統[1]』方案,使得 crash 防護這個想法真正被落實,但至今該方案的具體實現並沒有被開源。圈子裡也有一些開發朋友,基於這套方案設計並開源了自己的 「Baymax」,比如『老司機 iOS 周報第七期[2]』中曾提到的 BayMaxProtector[3]。
  • iOS實錄14:淺談iOS Crash
    日誌符號化、異常信息解讀、常見的Crash五部分介紹。線上APP的Crash還需要通過收集Crash機制來捕獲Crash並記錄在日誌中。dwarfdump --uuid xx.app.dSYM查看crash 日誌中的Incident Identifier (crash 文件的 UUID)6)使用命令,生成「可定位問題的crash文件」
  • iOS Crash 分析攻略
    如果這個version偏高,用系統的symbolicatecrash命令不能符號化日誌,一般如果看到是204, 改成104之後用symbolicatecrash就可以符號化了export DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer/alias symbolicatecrash="/Applications/Xcode.app/Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/symbolicatecrash
  • 淺談 iOS Crash(一)
    從中找到.dSYM和.app文件xcarchive所在的路徑一般在: /Users//Library/Developer/Xcode/Archives 目錄下3)獲取crash日誌文件4)將symbolicatecrash、.dSYM、.app、crash.crash拷貝到桌面下同一個文件夾下5)檢查 xx.app 和 xx.app.dSYM
  • 深入理解iOS Crash Log
    Exception Note中的代碼同樣在可以找到#define EXC_CORPSE_NOTIFY   13  Termination Reason提供的信息就更詳細一些了Termination Reason: Namespace SPRINGBOARD, Code 0x8badf00d0x8badf00d是一個很常見的
  • [譯]《iOS Crash Dump Analysis》- 符號化
    DWARF 數據,複製到另一個單獨的文件中。crash dump 告訴我們崩潰發生時程序在內存中的程序在內存中的地址信息。這告訴我們其他地址(TEXT)位置相對偏移量。在crash dump 報告的底部,我們有一行0x1008e0000 - 0x1008ebfff icdab_planets。所以 icdab_planets 的位置從 0x1008e0000 開始。
  • 關於面試總結13-app測試面試題
    本篇總結了app測試面試時候經常被問的10個相關問題1.什麼是activity?2.Activity生命周期?3.Android四大組件?4.app測試和web測試有什麼區別?5.android和ios測試區別?6.app出現ANR,是什麼原因導致的?7.App出現crash原因有哪些?
  • iOS從crash信息中查找崩潰原因
    前幾天發現了一款國內 FIR.im公司的產品bughd,因為伺服器在國內,crash的反饋速度應該很快,於是我就簡單的測試了一下,非常不錯。美中不足的是,因為沒有上傳程序的dSYM文件,所有bughd不能準確定位到崩潰的具體位置。雖然FIR給出了教程(iOS錯誤堆棧查找崩潰原因的方法),但是可能不是非常淺顯易懂,因此我要來個詳細的擴展教學!一步步來!
  • 5個月iOS開發入門總結(C++轉行iOS)
    此文主要總結下如何從 Windows C++ 開發轉到 iOS 開發。 信息《MacTalk人生元編程》,讓我對 Apple 有了第一次認識(以前只知道賈伯斯)。Xcode插件先安裝插件管理器 http://alcatraz.io/FuzzyAutocomplete 代碼自動完成的模糊匹配XToDo 查找代碼中的TODO標記KSImageNamed 代碼中寫[UIImage imageNamed:]時可預覽圖片XVim VIM模式VVDocumenter-Xcode 按///產生注釋XBookmark
  • App Crash原因及美團外賣Crash治理之路
    如果你的簡歷中提到有APP的測試經驗,面試中必問的一項就是,APP常見的Crash有哪些?以及引起的原因是什麼?
  • 手把手教你查看和分析iOS的crash崩潰異常
    如果在應用程式中接入了一些第三方的crash收集工具或者自建crash收集報告平臺的話將會很好的幫助開發者去分析和解決應用程式在線上運行的問題,當出現的崩潰問題能得到及時的解決和快速的修復時必將會大大的提升應用程式的用戶體驗。當前比較流行的crash收集分析工具很多都是基於開源的KSCrash代碼來進行封裝和改進的。
  • 聊聊蘋果的Bug - iOS 10 nano_free Crash
    它的crash堆棧大致為:這兩種跡象表明,這很可能是蘋果的bug。按流程,我們向蘋果提了bug report,並得到回覆:「iOS 10.2 Beta有穩定性提升」。終於等到iOS 10.2 Beta發布,我們重新統計了此類crash的系統版本分布。發現不僅在10.2 Beta正常,而且iOS 9也沒有crash。
  • iOS App 連續閃退時如何上報 crash 日誌
    crash 日誌能夠準確上報。App 無限循環 crash 時上報crash 日誌上報時,會發送網絡請求,如果請求成功之前 App 又發生 crash 該如何處理?用戶甚至會陷入無限循環的 crash 中。這篇文章介紹下出現第二種情況時,如何準確上報 crash 日誌。首先我們需要一種比較可靠的方式,可以在 app 啟動時判斷上次是否發生了啟動 crash。
  • 愛奇藝 Xcrash 是怎麼捕獲 crash 的
    Xcrash是愛奇藝在2019年4月開源在GitHub上的穩定性日誌收集框架,它能為android收集java crash、native crash、anr日誌。 4   //create and open log file  打開log文件 5   if((xc_crash_log_fd = xc_common_open_crash_log(xc_crash_log_pathname, sizeof(xc_crash_log_pathname), &xc_crash_log_from_placeholder)) < 0) goto
  • iOS App 後臺 Crash 調查
    Background Crash 調查以上是 iOS App 後臺運行的現有機制的簡單介紹,最近在調查一個 background crash,需要用到上述的後臺運行機制。用戶抱怨 App 的 cold start 非常頻繁,導致耗電嚴重。
  • [譯]《iOS Crash Dump Analysis》- Siri崩潰
    從上面對xpc的引用中我們可以看到。我們在試圖調用一個不再存在的對象的方法是什麼?crash dump 為我們提供了有用的答案:Application Specific Information: objc_msgSend() selector name: didUnlockScreen:現在我們必須對崩潰的三個方面 what, where 和 when 做出一個近似的解答。
  • xCrash 詳解與源碼分析
    xc_dl_create 是尋找到 so 被 mmap 所加載的虛擬地址,xc_dl_sym 是計算 so 中相應符號(函數)的虛擬地址。其主要是從 libc++.so 中查找符號 _ZNSt3__14cerrE,對的,就是 cerr ;從 libart.so 中查找符號 _ZN3art7Runtime9instance_E 以及 _ZN3art7Runtime14DumpForSigQuitERNSt3__113basic_ostreamIcNS1_11char_traitsIcEEEE 在進程虛擬空間中的地址
  • 有贊crash平臺符號化實踐
    缺點:這種方式也只能收集在手機設置中打開了上傳crash開關,以及TestFlight用戶的crash日誌。企業分發或 AdHoc 安裝,需要自行獲取崩潰日誌。信息不全,線程信息不夠。5.自己收集crash日誌,比如接入KSCrash、plcrashreporter等,但是要自己做符號化。
  • 有贊移動Crash平臺建設
    整個Crash上報過程、後續處理流程如下圖:為了避免crash堆棧的數據量過大,crash堆棧等長欄位存儲至HBase. Mysql中只要存儲前128預覽字符與Hbase中的row_key即可一、實現方案1.1 Crash發生時的攔截+上報Crash的攔截主要依靠各端系統的攔截機制。