10-14.【安全性与稳定性】SIGABRT、EXC_BAD_ACCESS、Fatal Error 的区别和常见原因是什么?

0 阅读3分钟

在 Swift 开发中,这三种崩溃类型分别代表了不同的“报错层级”:从 Swift 语言层的主动报错,到系统库的强制中断,再到硬件级别的内存错误。


1. Fatal Error (语言层的主动自杀)

Fatal Error 是 Swift 运行时(Runtime)发现程序进入了逻辑上“非法且不可挽回”的状态,为了防止数据损坏而主动触发的中断。

  • 发生机制:Swift 代码内部触发了 fatalError()assertionFailure()preconditionFailure() 等指令。

  • 控制台表现:你会看到非常明确的错误提示,如:

    Fatal error: Unexpectedly found nil while unwrapping an Optional value

  • 常见原因

    • 强解包失败:对 nil 使用了 !
    • 数组越界:访问了不存在的 index
    • 强制类型转换失败:使用 as! 将一个类型转为不兼容的类型。
    • 未实现的初始化器:在 required init(coder:) 中直接抛出 fatalError

2. SIGABRT (系统层的强制终止)

SIGABRTSignal Abort 的缩写。它通常不是由你的代码直接崩溃引起的,而是由操作系统或底层 C 库(如 libc)发现程序状态异常,发送了一个“终止信号”。

  • 发生机制:程序检测到了一个无法处理的错误(通常是底层逻辑冲突),于是调用了 abort() 函数。

  • 常见原因

    • Obj-C 异常未捕获:在混编项目中,Obj-C 抛出了 NSException(如调用了不存在的方法 unrecognized selector)。
    • Storyboard/XIB 连线断裂:删除了代码里的 @IBOutlet 但没删除 Storyboard 里的连线。
    • 多线程资源竞争:在某些情况下,系统库检测到不安全的并发访问。
    • 内存压力强杀:被 iOS 系统因占用内存过高或主线程卡死(Watchdog)而杀掉。

3. EXC_BAD_ACCESS (硬件/内核层的内存非法访问)

这是最底层、最难调试的崩溃。它意味着 CPU 试图访问一段它不应该访问或者不存在的内存地址。

  • 发生机制:内核检测到非法内存操作,向进程发送信号(通常对应 SIGSEGV 段错误或 SIGBUS 总线错误)。

  • 常见原因

    • 野指针 (Dangling Pointer) :访问了一个已经被释放(Deallocated)的对象。
    • 僵尸对象 (Zombies) :在 Swift 中较少见(由于 ARC),但在与 C/Obj-C 交互或使用 unowned 引用时可能发生。
    • 内存对齐/指针偏移错误:使用 UnsafePointer 进行手动内存管理时计算错误。
    • 栈溢出:无限递归导致栈空间耗尽。

总结与对比

特性Fatal ErrorSIGABRTEXC_BAD_ACCESS
报错源Swift 运行时 (Runtime)操作系统/底层库 (Signal)CPU/内核 (Hardware/Kernel)
可读性极高(带错误描述)一般(需查看回溯堆栈)极低(仅显示内存地址)
主要定位方式检查控制台 Log检查 Last Exception Backtrace开启 Address SanitizerZombies
典型案例nil 强解包连线错误、主线程卡死访问已释放内存

快速调试建议

  1. 遇到 Fatal Error:直接看控制台第一行,它会告诉你具体的错误原因和文件名。

  2. 遇到 SIGABRT:在 LLDB 终端输入 po $arg1(如果是 Obj-C 异常)或者查看左侧堆栈中最后一行你的代码。

  3. 遇到 EXC_BAD_ACCESS

    • Edit Scheme -> Diagnostics -> 勾选 Address Sanitizer
    • 如果是引用问题,勾选 Zombies(仅限模拟器)。