You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何检测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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 08:01:02