如何在Swift中正确设置PLCrashReporter的setCrashCallbacks回调?
Swift完全支持PLCrashReporter的崩溃回调能力,不需要切换到Objective-C实现,你遇到的回调不触发是使用方式错误导致的,具体问题和修复方案如下:
问题原因
- 回调结构体版本参数错误
你初始化PLCrashReporterCallbacks时version传了0,该参数必须传入PLCRASHREPORTER_CALLBACKS_VERSION常量,PLCrashReporter会校验版本号,不匹配的回调会被直接忽略。 - 回调上下文的接口限制
崩溃发生时处于信号处理的异步安全上下文,NSLog不属于异步安全函数,本身就无法在这个环境下正常输出日志,你用NSLog验证本身就不可行,甚至可能引发二次崩溃导致回调提前终止。 - 调用顺序适配
部分版本的PLCrashReporter要求先启用崩溃Reporter,再设置回调,否则回调不会被注册生效。
修复后的实现代码
import CrashReporter // 明确指定C函数调用约定 @convention(c) func handleSignalForCrashReport(_ siginfo: UnsafeMutablePointer<siginfo_t>?, _ context: UnsafeMutablePointer<ucontext_t>?, _ userInfo: UnsafeMutableRawPointer?) { // 用异步安全的write接口输出到stderr,不要用NSLog let logStr = "crash rep handleSignalForCrashReport\n" guard let data = logStr.data(using: .utf8) else { return } data.withUnsafeBytes { buf in guard let baseAddr = buf.baseAddress else { return } _ = write(STDERR_FILENO, baseAddr, data.count) } // 你的自定义逻辑,注意只能调用异步安全的C接口,不能使用任何OC/Swift的Foundation方法、申请内存等操作 } class AppDelegate: UIResponder, UIApplicationDelegate { func setupCrashReporter() { let config = PLCrashReporterConfig(signalHandlerType: .mach, symbolicationStrategy: .all) guard let crashReporter = PLCrashReporter(configuration: config) else { NSLog("crash reporter init failed") return } do { // 先启用Reporter try crashReporter.enableAndReturnError() NSLog("crash reporter enabled") } catch { NSLog("enable crash reporter failed: \(error)") return } // 初始化回调结构体,传入正确的版本号 var callbacks = PLCrashReporterCallbacks( version: UInt16(PLCRASHREPORTER_CALLBACKS_VERSION), context: nil, handleSignal: handleSignalForCrashReport ) // 启用后再设置回调 crashReporter.setCrash(&callbacks) // 原有 pending crash report 处理逻辑不变 if crashReporter.hasPendingCrashReport() { do { let data = try crashReporter.loadPendingCrashReportDataAndReturnError() let report = try PLCrashReport(data: data) guard let reportText = PLCrashReportTextFormatter.stringValue(for: report, with: PLCrashReportTextFormatiOS) else { NSLog("report text parse failed") return } NSLog("previous crash report: \(reportText)") try crashReporter.purgePendingCrashReportAndReturnError() } catch { NSLog("load pending report failed: \(error)") } } } func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { setupCrashReporter() return true } }
注意事项
- 崩溃回调内只能执行极简的异步安全逻辑,不要做复杂操作、不要申请内存、不要调用任何Swift/Objective-C的高层API,否则会引发二次崩溃,导致回调逻辑无法完整执行。
- 验证回调是否触发可以通过直接写文件的方式,或者在Xcode调试环境下触发崩溃,看stderr的输出即可。
内容的提问来源于stack exchange,提问作者nacho4d
相关产品推荐
相关产品推荐

