不少人在开发中都会遇到 EXC_BAD_ACCESS ,很遗憾,这一次 Xcode 不会给出任何详细的解决方案。
通常来说,这是由于内存错误造成的。简单来说就是你创建了对象 A,但在后来访问的时候,内存里 A 这块区域已经被系统挪做他用了,比如放了对象 B 在这里——你的 A 只剩下指针,实际内容已经不存在了。
这时就会出现类似这样的崩溃:
1 |
-[__NSCFType dismissAuxiliaryWindows]: unrecognized selector sent to instance 0x6000030010e0 |
由于实际对象已经变更,Swift 编译器却并不知道,结果自然就是一个“未知的 Selector”了。
尝试解决它
一开始,无知的我想要去找到这个“instance”到底是谁,于是我开始用这个方法打印程序里的对象:
1 2 |
var a = 1 print(Unmanaged.passUnretained(a).toOpaque()) |
嗯,后来我发现这是一个无止境的工作……但报错变得有意思了:
1 |
-[_TtGCs23_ContiguousArrayStorageSS_$ dismissAuxiliaryWindows]: unrecognized selector sent to instance 0x600003004240 |
这让我更加摸不着头脑。
再后来,干脆就没报错了,就是 EXC_BAD_ACCESS ,于是内存地址对比也就到此为止了……
正确的做法
总之,Xcode 还是有工具来处理这种情况的——虽然不一定是百发百中,但至少能增加一点排错的线索,我们到 Xcode 左上角选择 Edit Scheme,编辑当前程序的执行选项:
在打开的页面选“Run”,选右侧“Diagnosis”选项卡,勾选下方的“Zombie Object”。
Zombie Object 模式:僵尸模式,在这个模式下你的程序不会真的释放需要被释放的内存,即使对象 A 已经没有引用,它也会被保留在内存当中,这样就避免了系统将其他对象的内存写到同一位置,一旦 EXC_BAD_ACCESS 发生,Xcode 将能够发现程序原本想要访问的是哪个对象。
再次运行程序,触发崩溃,我们得到了新的报错:
1 |
*** -[NSSpellChecker dismissAuxiliaryWindows]: message sent to deallocated instance 0x600003010b40 |
显然,这次就明确多了,虽然不知为何,但这次的 EXC_BAD_ACCESS 是由于程序访问了一个 NSSpellChecker 对象导致的,这下就大大缩小了排错范围,运气好的话应该很快就能定位问题所在了。
总之,最终记得关闭这个模式,不然程序占用内存会无休止地增加下去……
参考文献
本文由 落格博客 原创撰写:落格博客 » Swift Debug EXC_BAD_ACCESS in AppDelegate
转载请保留出处和原文链接:https://www.logcg.com/archives/3313.html