Swift实现iOS简易崩溃检测:信号处理器未触发求排查
iOS简易崩溃检测实现问题:信号处理器未被调用
我正尝试实现简易的iOS崩溃检测功能。十年前用Objective-C借助UncaughtExceptionHandler就能轻松搞定,但Swift需要更多处理,我不确定当前思路是否正确。
我参考其他方案和评论,尝试了以下基于信号的实现,却发现处理器并未被调用。请问我遗漏了什么或哪里操作有误?
import Foundation class CrashDetection: NSObject { private static let observedSignals = [SIGABRT, SIGILL, SIGSEGV, SIGFPE, SIGBUS, SIGPIPE, SIGTRAP] private static var previousSignalHandlers: [Int32:(@convention(c) (Int32) -> Void)] = [:] @objc class func start() { NSSetUncaughtExceptionHandler(CrashDetection.recieveException) observedSignals.forEach { signalType in let oldHandler = signal(signalType, CrashDetection.recieveSignal) CrashDetection.previousSignalHandlers[signalType] = oldHandler } } private static let recieveException: @convention(c) (NSException) -> Swift.Void = { (recieveException) -> Void in UserDefaults.standard.setValue(true, forKey: "crashDidOccur") } private static let recieveSignal: @convention(c) (Int32) -> Void = { (recievedSignal) -> Void in UserDefaults.standard.setValue(true, forKey: "crashDidOccur") NSSetUncaughtExceptionHandler(nil) observedSignals.forEach { signalType in signal(signalType, previousSignalHandlers[signalType]) } } }
问题分析与解决方案
你的实现存在几个关键问题,导致信号处理器未被正确触发:
信号处理流程错误
在recieveSignal中,你直接恢复了所有信号的旧处理器,但没有重新发送当前接收到的信号。系统在触发信号时会调用当前注册的处理器,你恢复旧处理器后没有让系统继续处理该信号,导致流程中断,同时你的逻辑也可能因为进程即将终止而无法完全执行。UserDefaults写入不可靠
崩溃发生时,App进程可能很快被系统终止,UserDefaults的自动同步可能来不及完成,导致标记无法持久化。必须手动调用synchronize()强制写入(尽管iOS 10+官方不推荐,但崩溃场景下是必要的)。未覆盖Swift运行时崩溃
Swift的很多崩溃(如数组越界、强制解包nil)属于Mach异常,不会触发传统的POSIX信号,也无法被NSSetUncaughtExceptionHandler捕获,必须额外注册Mach异常处理。函数名拼写错误
recieveException和recieveSignal的正确拼写是receive,虽然这不会直接导致处理器失效,但属于代码规范问题,可能引发后续维护隐患。
修正后的代码
import Foundation import MachO class CrashDetection: NSObject { private static let observedSignals = [SIGABRT, SIGILL, SIGSEGV, SIGFPE, SIGBUS, SIGPIPE, SIGTRAP] private static var previousSignalHandlers: [Int32: (@convention(c) (Int32) -> Void)?] = [:] private static var machExceptionPort: mach_port_t = 0 @objc class func start() { // 捕获Objective-C异常 NSSetUncaughtExceptionHandler(receiveException) // 注册信号处理器 observedSignals.forEach { signalType in let oldHandler = signal(signalType, receiveSignal) previousSignalHandlers[signalType] = oldHandler } // 注册Mach异常处理器,覆盖Swift运行时崩溃 registerMachExceptionHandler() } private static let receiveException: @convention(c) (NSException) -> Void = { exception in // 记录崩溃标记并强制同步 UserDefaults.standard.setValue(true, forKey: "crashDidOccur") UserDefaults.standard.synchronize() // 恢复原异常处理器并重新抛出异常 NSSetUncaughtExceptionHandler(nil) exception.raise() } private static let receiveSignal: @convention(c) (Int32) -> Void = { signal in // 记录崩溃标记并强制同步 UserDefaults.standard.setValue(true, forKey: "crashDidOccur") UserDefaults.standard.synchronize() // 恢复当前信号的原处理器 if let oldHandler = previousSignalHandlers[signal] { signal(signal, oldHandler) } else { signal(signal, SIG_DFL) } // 重新发送信号,让系统完成崩溃流程 raise(signal) } private static func registerMachExceptionHandler() { // 创建异常接收端口 let port = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE) mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND) // 定义异常处理闭包 let exceptionHandler: exception_handler_t = { (_, _, _, _, _, _) -> kern_return_t in UserDefaults.standard.setValue(true, forKey: "crashDidOccur") UserDefaults.standard.synchronize() mach_port_deallocate(mach_task_self(), port) return KERN_FAILURE } // 设置异常监听端口 exception_set_port(mach_task_self(), EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION, port) machExceptionPort = port // 启动后台线程监听Mach异常 Thread.detachNewThreadSelector(#selector(listenMachExceptions), toTarget: self, with: nil) } @objc private static func listenMachExceptions() { var message = mach_msg_header_t() while true { let result = mach_msg(&message, MACH_RCV_MSG, 0, MemoryLayout<mach_msg_header_t>.size, machExceptionPort, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL) if result != KERN_SUCCESS { break } } } }
关键改进说明
- 新增Mach异常处理逻辑,能够捕获Swift特有的运行时崩溃;
- 调整信号处理流程,先完成标记写入,再恢复原处理器并重新发送信号,确保系统能正常完成崩溃流程;
- 加入
UserDefaults.standard.synchronize(),保证崩溃标记能被持久化; - 修正函数名拼写错误,提升代码规范性。
内容的提问来源于stack exchange,提问作者Helge Becker
相关产品推荐
相关产品推荐

