在iOS开发中,EXC_BAD_ACCESS和僵尸对象(Zombie Objects)有关联,但不是一回事。
EXC_BAD_ACCESS是一种常见的崩溃类型,表示程序试图访问已经释放的内存地址。这通常是因为对象已经被释放(即内存已经被回收),但程序仍然试图向该对象发送消息或者访问其属性。
僵尸对象是一种调试技术,用于帮助定位EXC_BAD_ACCESS错误。当启用僵尸对象时,对象在释放后不会被立即回收,而是被标记为僵尸对象。之后如果程序试图访问这个对象,就会触发一个已知的行为(比如抛出异常或打印日志),从而帮助我们定位问题。
因此,EXC_BAD_ACCESS是错误类型,而僵尸对象是调试这种错误的一种工具。
总结:
-
EXC_BAD_ACCESS:表示访问了已释放内存的错误。
-
僵尸对象:一种调试模式,用于检测和定位对已释放对象的访问。
在开发过程中,开启僵尸对象调试可以帮助我们快速定位导致EXC_BAD_ACCESS的代码。
但它们有密切关联:
EXC_BAD_ACCESS(坏访问)
-
是什么:一种内存访问错误导致的崩溃
-
原因:试图访问已释放或无效的内存地址
-
常见场景:
-
向已释放的对象发送消息
-
访问已释放的内存
-
栈溢出或数组越界
-
野指针访问
-
僵尸对象(Zombie Objects)
-
是什么:一种调试技术/对象状态
-
作用:帮助检测和调试EXC_BAD_ACCESS错误
-
原理:对象释放时不立即回收内存,而是标记为"僵尸",当再次被访问时触发可识别的错误
关键区别
| 方面 | EXC_BAD_ACCESS | 僵尸对象 |
|---|---|---|
| 性质 | 崩溃类型/错误 | 调试技术/对象状态 |
| 关系 | 症状(结果) | 诊断工具(手段) |
| 触发时机 | 访问无效内存时 | 访问已释放对象时 |
实际联系
// 示例:典型的EXC_BAD_ACCESS场景
class MyClass {
func doSomething() {}
}
var obj: MyClass? = MyClass()
obj?.doSomething() // ✅ 正常访问
obj = nil // 释放对象
// 如果不启用僵尸对象检测:
obj?.doSomething() // ❌ 可能直接EXC_BAD_ACCESS崩溃
// 信息很少,难以调试
// 如果启用僵尸对象检测:
// obj?.doSomething() 会抛出明确的错误信息:
// "message sent to deallocated instance 0x..."
如何启用僵尸对象调试
-
Xcode中:
-
Scheme设置 → Run → Diagnostics
-
勾选"Zombie Objects"
-
-
代码中启用:
// 环境变量方式
// 在Scheme的Arguments中添加:
// NSZombieEnabled = YES
// 或 MallocStackLogging = YES
调试建议
-
常规调试:
-
开启僵尸对象检测
-
使用Address Sanitizer
-
检查循环引用和弱引用
-
-
常见修复:
-
避免悬垂指针
-
合理使用弱引用(weak)
-
注意block中的self捕获
-
正确管理NSTimer等强引用对象
-
总结:EXC_BAD_ACCESS是"病",僵尸对象是"诊断工具"。不是一回事,但僵尸对象专门用来诊断EXC_BAD_ACCESS这类内存问题。