作为一枚iOS开发的新手菜鸟,最近掉了一个比较难查的坑,不过最终还是爬出来了,而且其实非常简单,把过程记录一下。
需求是要做一个轮播。我的处理方法是写了个 SwitchBanner 类。在 viewDidLoad 中
SwitchBanner* banner = [SwitchBanner initWithType:@"首页" wrapper:wrapper];
[banner fetchNew];
大致的想法就是传入一个 UIView 作为容器来初始化,然后提供一个更新内容的方法。
fetchNew 方法里面会去拿数据,然后重新把轮播画出来。
接着因为我希望每一帧画面点击了之后能够跳转到相应的界面。所以在 SwitchBanner 内部的 render 方法中,对每一帧的视图(这里使用 UIButton 来做)加了一个事件。
[button addTarget:self action:@selector(itemTapped:)
forControlEvents:UIControlEventTouchUpInside];
看起来没有什么问题(有经验的同学内心已经在笑了),运行时候也可以正常的显示,但是一旦点击横幅,就会出现 EXC_BAD_ACCESS 的错误,并且完全没有上下文,所以一头雾水不知从何解决。
在网上搜索一番之后找到解决之道。
首先在 Xcode 中打开 Product->Scheme->Edit Scheme,快捷键是⌘<。在 "Diagnostics" 标签中开启 "Enable Zombie Objects" 选项,以及 "Malloc Stack" 选项。EXC_BAD_ACCESS 的产生是由于向未被初始化、或者已经被回收的内存发送消息,通常大家遇到的都是后者。
开启 "Zombie Objects" 后,对象不会被真正释放,而是转为一枚僵尸对象,这样你就可以看看僵尸先生生前大概是个啥模样。开启之后,错误抛出的时候会在控制台打印出如下信息
-[SwitchBanner performSelector:withObject:withObject:]:
message sent to deallocated instance 0x7f8cb9fa5550
"Malloc Stack" 选项可以让系统把内存分配的过程都记录下来以便进一步看问题。开启之后在抛出异常时就可以在控制台使用下面的语句看到对象是如何被分配的了
command script import lldb.macosx.heap
malloc_info --stack-history 0x7f8cb9fa5550
好了,问题找到了,就是我们的 SwitchBanner 实例被回收了。原因很简单,因为他是在 viewDidLoad 内部定义的,方法执行完毕自然就被回收了。解决方式就是让 viewController 将其作为属性持有住就行了:
@property (nonatomic, strong) SwitchBanner* banner;
什么嘛,很简单嘛。问题解决之后记得把那两个调试选项都关掉,养成好习惯。
以上。
网友评论