如何检测iOS应用此前是否崩溃?Swift与obj-c无第三方库实现
通用崩溃检测方案(Swift & Objective-C)
核心实现思路
要覆盖所有崩溃场景,需要同时处理三类问题:Objective-C 异常、Swift 致命错误(如强制解包nil)对应的信号、以及系统级崩溃信号。同时要保证不影响第三方崩溃库,需采用链式处理方式,保留原有处理器逻辑。
具体实现步骤及代码
1. 状态标记与崩溃次数统计逻辑
启动时先检查崩溃标记:
- 若存在标记,说明上次启动崩溃,将崩溃次数+1并清除标记
- 若不存在标记,设置「已启动」标记
- 正常退出时清除「已启动」标记
Objective-C 实现
// CrashTracker.h #import <Foundation/Foundation.h> @interface CrashTracker : NSObject + (void)setup; @end // CrashTracker.m #import "CrashTracker.h" #include <signal.h> static NSString *const kAppLaunchedMarker = @"kAppLaunchedMarker"; static NSString *const kCrashCountKey = @"kCrashCountKey"; static volatile sig_atomic_t didCrash = 0; static NSUncaughtExceptionHandler *originalExceptionHandler; static void (*originalSignalHandlers[NSIG])(int); // 信号处理函数 static void handleSignal(int signal) { didCrash = 1; // 调用原信号处理器,保证第三方库逻辑正常执行 if (originalSignalHandlers[signal]) { originalSignalHandlers[signal](signal); } } // OC异常处理函数 static void handleException(NSException *exception) { didCrash = 1; // 调用原异常处理器 if (originalExceptionHandler) { originalExceptionHandler(exception); } } @implementation CrashTracker + (void)setup { // 启动时检查崩溃状态 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; BOOL wasLaunched = [defaults boolForKey:kAppLaunchedMarker]; if (wasLaunched) { // 上次启动未正常退出,判定为崩溃 NSInteger crashCount = [defaults integerForKey:kCrashCountKey]; [defaults setInteger:crashCount + 1 forKey:kCrashCountKey]; [defaults synchronize]; } // 设置已启动标记 [defaults setBool:YES forKey:kAppLaunchedMarker]; [defaults synchronize]; // 注册OC异常处理器 originalExceptionHandler = NSGetUncaughtExceptionHandler(); NSSetUncaughtExceptionHandler(&handleException); // 注册常见崩溃信号处理器 int signals[] = {SIGABRT, SIGILL, SIGSEGV, SIGFPE, SIGBUS, SIGTRAP}; for (int i = 0; i < sizeof(signals)/sizeof(signals[0]); i++) { int signal = signals[i]; originalSignalHandlers[signal] = signal(signal, handleSignal); } // 监听应用正常退出事件 [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillTerminateNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) { [defaults setBool:NO forKey:kAppLaunchedMarker]; [defaults synchronize]; }]; } @end
Swift 调用(或纯Swift实现)
可以直接调用OC工具类,也可以基于C函数实现纯Swift版本,以下是调用示例:
// AppDelegate.swift import UIKit @main class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // 初始化崩溃跟踪 CrashTracker.setup() return true } }
2. 关键注意事项
- 信号处理函数的安全性:信号处理函数中不能调用Objective-C/Swift API(如直接操作UserDefaults),因此用
volatile sig_atomic_t类型的全局变量标记崩溃,启动时统一处理统计 - 链式调用原处理器:必须保存并调用原来的异常/信号处理器,否则会覆盖第三方崩溃库的逻辑
- 正常退出的边界情况:若应用被系统强制杀死(如后台内存回收),
UIApplicationWillTerminateNotification不会触发,可能导致误判为崩溃,这种情况属于系统限制,无法完全避免
对第三方崩溃库的影响
只要按照上述链式处理的方式实现,就不会影响第三方崩溃库的功能:
- 第三方库(如Firebase Crashlytics、Bugly)同样会注册异常处理器和信号处理器,我们保存并调用原处理器,相当于把自己的逻辑加入到崩溃处理链中
- 如果直接替换处理器而不调用原逻辑,会导致第三方库无法捕获崩溃
第三方崩溃库的核心实现原理
主流第三方崩溃库的实现逻辑比基础方案更复杂,核心包括:
- 多维度崩溃捕获:同时处理OC异常、信号、Mach异常(更底层的系统崩溃),覆盖所有崩溃场景
- 堆栈生成与解析:崩溃时通过
backtrace()等函数获取堆栈地址,结合符号表解析出具体代码位置 - 上下文保存:记录崩溃时的设备信息、应用状态、日志等,便于问题定位
- 异步上传:崩溃信息保存到本地,下次启动时异步上传到服务器
- 线程安全处理:保证崩溃捕获逻辑在多线程环境下的稳定性,避免二次崩溃
内容的提问来源于stack exchange,提问作者Keer
相关产品推荐
相关产品推荐

