// // CrashHandler.m // SellyCloudSDK_Example // // Created by Caleb on 24/10/25. // Copyright © 2025 Caleb. All rights reserved. // #import "CrashHandler.h" #include #include #include #import @interface CrashHandler () @end void HandleException(NSException *exception); void SignalHandler(int signal); @implementation CrashHandler + (void)load { [self installCrashHandler]; } + (void)installCrashHandler { // 1. 捕获 Objective-C 异常 NSSetUncaughtExceptionHandler(&HandleException); // 2. 捕获 Unix 信号 signal(SIGABRT, SignalHandler); signal(SIGILL, SignalHandler); signal(SIGSEGV, SignalHandler); signal(SIGFPE, SignalHandler); signal(SIGBUS, SignalHandler); signal(SIGPIPE, SignalHandler); } + (NSString *)crashLogPath { NSString *doc = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject; return [doc stringByAppendingPathComponent:@"crash.log"]; } + (void)saveCrashToLocal:(NSString *)txt { // DDLogInfo(@"####crashed:"); NSFileManager* fileManager = [NSFileManager defaultManager]; BOOL isDir = NO; BOOL dataIsDir = NO; // fileExistsAtPath 判断一个文件或目录是否有效,isDirectory判断是否一个目录 NSString* dirPath = [applicationDocumentsDirectory() stringByAppendingPathComponent:@"ErrorLog"]; NSString* filePath = [dirPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.txt", CarshDate()]]; BOOL existed = [fileManager fileExistsAtPath:dirPath isDirectory:&isDir]; BOOL dataExisted = [fileManager fileExistsAtPath:filePath isDirectory:&dataIsDir]; if (!(isDir == YES && existed == YES)) { //如果文件夹不存在 [fileManager createDirectoryAtPath:dirPath withIntermediateDirectories:YES attributes:nil error:nil]; } if (!(dataIsDir == YES && dataExisted == YES)) { [txt writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil]; } } // 沙盒地址 NSString* applicationDocumentsDirectory(void) { return [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]; } NSString* CarshDate(void) { NSDateFormatter* formatter = [NSDateFormatter new]; [formatter setDateFormat:@"yyyy-MM-dd_HHmmss"]; return [formatter stringFromDate:[NSDate date]]; } @end // 崩溃时的回调函数 void HandleException(NSException* exception) { NSArray* arr = [exception callStackSymbols]; // 崩溃的原因 可以有崩溃的原因(数组越界,字典nil,调用未知方法...) 崩溃的控制器以及方法 NSString* reason = [exception reason]; NSString* name = [exception name]; NSDictionary* userInfo = [exception userInfo]; NSDictionary* infoDictionary = [[NSBundle mainBundle] infoDictionary]; // app名称 NSString* app_Name = [infoDictionary objectForKey:@"CFBundleDisplayName"]; // app版本 NSString* app_Version = [infoDictionary objectForKey:@"CFBundleShortVersionString"]; // app build版本 NSString* app_build = [infoDictionary objectForKey:@"CFBundleVersion"]; //手机型号 NSString* phoneModel = [[UIDevice currentDevice] model]; //手机系统版本 NSString* phoneVersion = [[UIDevice currentDevice] systemVersion]; NSString* txt = [NSString stringWithFormat:@"========异常错误报告========\n\n" @"手机型号:%@ 版本:%@\n" @"App名称:%@ 版本:%@(%@)\n\n" @"name:%@\n" @"userInfo:%@\n\n" @"reason:\n%@\n\n" @"callStackSymbols:\n%@", phoneModel, phoneVersion, app_Name, app_Version, app_build, name, userInfo, reason, [arr componentsJoinedByString:@"\n"]]; [CrashHandler saveCrashToLocal:txt]; } void SignalHandler(int signal) { // 获取调用栈 void *callstack[128]; int frames = backtrace(callstack, 128); char **strs = backtrace_symbols(callstack, frames); NSMutableString *stackStr = [NSMutableString string]; for (int i = 0; i < frames; i++) { [stackStr appendFormat:@"%s\n", strs[i]]; } NSString *content = [NSString stringWithFormat:@"Signal %d was raised.\nStack:\n%@", signal, stackStr]; NSString *path = [CrashHandler crashLogPath]; [content writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:nil]; free(strs); }