从App随机崩溃(KVO_IS_RETAINING_ALL_OBSERVERS...)到彻底修复的回顾(上)

版权声明:大熊猫猪·侯佩原创或翻译作品.谢绝转载! hopy https://blog.csdn.net/mydo/article/details/84560577

本博通过具体的实例向大家展示如何一步步通过合(xia)理(cai)分析找出问题的罪魁祸首,适合有一定调试经验的童鞋,也欢迎各路神仙和小白围观,本人水平一般,欢迎指正,但我就是不改.

崩溃特征

0.崩溃的类型是EXC_BAD_ACCESS

1.App崩溃虽说是随机的,但在一定次数的相同UI操作后必定会发生,但操作的次数不固定。

2.从Xcode里观察崩溃时的源代码,"按道理"完全不可能崩溃

3.而且特别有意思的地方是:当你在Xcode中开启了"僵尸对象"后,崩溃现象神奇的消失了

4.每次崩溃的堆栈痕迹(以下简称栈迹,觉得别扭么?习惯就好)都不一样,但翻来覆去就是那几种

5.有一次崩溃后栈迹里完全看不到函数符号名称,只有一些地址,这说明堆栈记录破坏的很厉害。还有一次更离谱:崩溃App直接从真机里退出了,留下一脸懵逼的Xcode傻傻的在那里完全没有反应…

用栗子说话

有点兴趣了么?下面是其中一个崩溃时的栈迹(是的,我也记不得调试了多少次…):

* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x5c89a7ce0)
    frame #0: 0x00000001c223fdac libobjc.A.dylib`object_isClass + 16
    frame #1: 0x00000001c3ae6898 Foundation`KVO_IS_RETAINING_ALL_OBSERVERS_OF_THIS_OBJECT_IF_IT_CRASHES_AN_OBSERVER_WAS_OVERRELEASED_OR_SMASHED + 68
    frame #2: 0x00000001c3ae402c Foundation`NSKeyValueWillChangeWithPerThreadPendingNotifications.llvm.9271023180890826241 + 304
    frame #3: 0x00000001c769b1dc QuartzCore`CAAnimation_setter(CAAnimation*, unsigned int, _CAValueType, void const*) + 156
    frame #4: 0x00000001c769aa28 QuartzCore`-[CAAnimation setBeginTime:] + 48
    frame #5: 0x00000001c7674218 QuartzCore`CA::Layer::commit_animations(CA::Transaction*, double (*)(CA::Layer*, double, void*), void (*)(CA::Layer*, CA::Render::Animation*, void*), void (*)(CA::Layer*, __CFString const*, void*), void*) + 576
    frame #6: 0x00000001c7673c04 QuartzCore`CA::Layer::commit_if_needed(CA::Transaction*, void (*)(CA::Layer*, unsigned int, unsigned int, void*), void*) + 420
    frame #7: 0x00000001c7673b64 QuartzCore`CA::Layer::commit_if_needed(CA::Transaction*, void (*)(CA::Layer*, unsigned int, unsigned int, void*), void*) + 260
    frame #8: 0x00000001c7673b64 QuartzCore`CA::Layer::commit_if_needed(CA::Transaction*, void (*)(CA::Layer*, unsigned int, unsigned int, void*), void*) + 260
    frame #9: 0x00000001c7673b64 QuartzCore`CA::Layer::commit_if_needed(CA::Transaction*, void (*)(CA::Layer*, unsigned int, unsigned int, void*), void*) + 260
    frame #10: 0x00000001c7673b64 QuartzCore`CA::Layer::commit_if_needed(CA::Transaction*, void (*)(CA::Layer*, unsigned int, unsigned int, void*), void*) + 260
    frame #11: 0x00000001c7673b64 QuartzCore`CA::Layer::commit_if_needed(CA::Transaction*, void (*)(CA::Layer*, unsigned int, unsigned int, void*), void*) + 260
    frame #12: 0x00000001c7673b64 QuartzCore`CA::Layer::commit_if_needed(CA::Transaction*, void (*)(CA::Layer*, unsigned int, unsigned int, void*), void*) + 260
    frame #13: 0x00000001c7673b64 QuartzCore`CA::Layer::commit_if_needed(CA::Transaction*, void (*)(CA::Layer*, unsigned int, unsigned int, void*), void*) + 260
    frame #14: 0x00000001c7673b64 QuartzCore`CA::Layer::commit_if_needed(CA::Transaction*, void (*)(CA::Layer*, unsigned int, unsigned int, void*), void*) + 260
    frame #15: 0x00000001c7673b64 QuartzCore`CA::Layer::commit_if_needed(CA::Transaction*, void (*)(CA::Layer*, unsigned int, unsigned int, void*), void*) + 260
    frame #16: 0x00000001c75c726c QuartzCore`CA::Context::commit_transaction(CA::Transaction*) + 3240
    frame #17: 0x00000001c75f504c QuartzCore`CA::Transaction::commit() + 608
    frame #18: 0x00000001f011e01c UIKitCore`_afterCACommitHandler + 256
    frame #19: 0x00000001c2fff7a8 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 32
    frame #20: 0x00000001c2ffa43c CoreFoundation`__CFRunLoopDoObservers + 412
    frame #21: 0x00000001c2ffa9dc CoreFoundation`__CFRunLoopRun + 1264
    frame #22: 0x00000001c2ffa1cc CoreFoundation`CFRunLoopRunSpecific + 436
    frame #23: 0x00000001c5271584 GraphicsServices`GSEventRunModal + 100
    frame #24: 0x00000001f00f5054 UIKitCore`UIApplicationMain + 212
  * frame #25: 0x000000010062b104 LightHabit`main at AppDelegate.swift:14
    frame #26: 0x00000001c2ababb4 libdyld.dylib`start + 4
(lldb) 

估计一看见上面这坨东东,围观的人会散去很多…

在这里插入图片描述

留下来的同志请注意,上面是我特意挑的一个特别短的。

实际上在Xcode界面中你看到的比这26条要少,原因是Xcode只会列出和你源代码相关的栈迹,误解的关键就在这:

你看到崩溃对应源代码中那行,实际不是真正崩溃的地方!!!

所以某种意义上来说App崩溃并不是你那行源代码引起的,但别高兴太早 : 你也绝逃脱不了干系!!!

在这里插入图片描述

先解决简单问题

简单的问题就是为什么开启了僵尸对象后,崩溃反而消失了???

原因简单来说就是崩溃和僵尸对象没有半毛钱关系!

开启僵尸对象的作用是用来检测已释放对象的再使用行为,它的一个副作用是系统不再会清除已释放对象,这样一来你再给它发消息,就会马上被发现!

就是因为对象不会再释放导致了崩溃不会再出现!!!

所以推论如下:凡是打开僵尸对象后反而啥问题没有的,恰恰表示并不是僵尸对象引起的问题!

我们隐约可以猜到 : 是某个不该释放的神秘对象被释放才导致了崩溃

只要猜到上面这条就行了,这里不再深入研究,有兴趣的童鞋可以看相关案例:

https://github.com/AFNetworking/AFNetworking/issues/3710

其中最关键的段落摘录如下:

That’s some interesting data:
NSProgress instance are sometimes reassigned an address previously used by another NSProgress instance: for example, 0x7ffa6a119730 is assigned to one download progress, then one upload progress, then another download progress (where the crash happens). This explains why running with zombies enabled made the crash disappear: addresses are never reused because deallocated objets turn into zombies and keep their original address forever.
When the same address (0x7ffa6a119730) is reused, the NSProgress instance has an observer right after its creation. That observer is 0x7ffa68c0bab0, an old AFURLSessionManagerTaskDelegate instance.

供大家自己品味。

把简单的解决,剩下的就不简单了

回到上面那个栗子,注意观察其中第0帧( frame #0),真正App崩溃点在这里:

就在判断对象的类型时(object_isClass),而这个对象已经可耻的匿鸟。

为什么会去调用object_isClass呢?再看之前的帧大多都是和界面相关的,这意味着和界面改变有很大因缘

最后注意前面两帧的内容:

frame #1: 0x00000001c3ae6898 Foundation`KVO_IS_RETAINING_ALL_OBSERVERS_OF_THIS_OBJECT_IF_IT_CRASHES_AN_OBSERVER_WAS_OVERRELEASED_OR_SMASHED + 68

frame #2: 0x00000001c3ae402c Foundation`NSKeyValueWillChangeWithPerThreadPendingNotifications.llvm.9271023180890826241 + 304

是不是感觉到了什么?是的,无数线索都指向一个源头,心中呐喊着那个词: Notifications!!!

没有更多推荐了,返回首页

私密
私密原因:
请选择设置私密原因
  • 广告
  • 抄袭
  • 版权
  • 政治
  • 色情
  • 无意义
  • 其他
其他原因:
120
出错啦
系统繁忙,请稍后再试