Zombie调试及教训
最近App碰到一个问题, logout后再login大概率crash
用真机跑几次收集crashlog, 死在主线程上,栈顶是这个:
QuartzCore
CA::release_objects(X::List<void const*>*)
好高深的样子...看到QuartzCore,最近没有人去处理2D图像啊。搁置。
搁置两天crash频发,不得不硬着头皮上。使用Analyze蹦出大堆warning(各种API过期..),专门扫了下Memory Warning没有线索
忽然想起Xcode特有的Zombie方法--Object正常release后变成僵尸/Zombie状态,等到下次过度release时发出警告
于是Edit Scheme, Run/Debug下打开Objective-C Enable Zombie Objects(第一次顺手打开Malloc的三个Enable,结果刚跑起来就挂了,放弃),重新跑一边,挂了之后console果然告知在内存0xABCDEF处出问题了。打印之,是CALayer的析构函数。
约等于什么都没说。算了上大杀器吧,Instruments。检测所有Allocations。没错,Launch Configuration for Heap Allocations 里有Enable NSZombie detection的选项。打开之,重跑一边。这次就慢多了。等跑挂了,在顶上Allocatons的视图最右侧找一个小小的NSZombie的旗。查看相关信息,发现最早是在第九秒多分配的,中间retain release多次,即使有stace backtrace也都是系统调用,没线索。
没辙,记录下时间,然后看Allocations>Allocations List>All Allocations 全部分配记录里慢慢翻(其实就是History的上级,点一下就自动定位过来了,当时笨了)。找到后发现前一个就是App自己实现的ViewController的函数loadview了。
不过这个loadview里创建控件太多,没法确定是哪一个(后来复盘时发现,紧接着这个CALayer的创建,接下来有NSTextStorage NSString等普遍的东西中间还夹杂重要的提示NSTextField的创建设置),只好自己去loadview里面去看alloc 跟release是不是匹配。吐血啊,中间想着如果还是找不出来,那就对这个ViewController上ARC算了,由系统来匹配。最后发现loadview里是匹配的(!!!), 果断切换到dealloc里看那些指向这些控件的类变量的release。果然,对比生命发现,里面所有的textfield声明成assign的,其他label,textview都是retain的;最近一次增补时,把assign的textfield在dealloc函数里多释放了一次。
教训
1. 代码一致,否则就是在精力不济时坑自己。
2. 上ARC吧,代码量增加的基础上MRC成本不低了。Ruby作者说过,程序员能力在代码行数方面是差不多的,所以更简洁更强大的表达能增强程序员的能力(大意). ARC在这个角度上能减少不少release行。
2014.10.28
浙公网安备 33010602011771号