EXC_BAD_ACCESS算是一个比较常见的错误,大部分情况下,它出现在某个对象还未初始化或已被释放后,还去试图访问这个对象的时候,即是在出现悬挂指针的时候(当然也有非悬挂指针导致的EXC_BAD_ACCESS)。在MRC机制下,EXC_BAD_ACCESS相对会出现得比较多,毕竟手动管理引用计数比较可能会数错。

  在ARC机制下就出现得比较少了,我自己就遇到过几次,可惜基本都忘了记下来,今天就准备试一试如何制造出一个EXC_BAD_ACCESS。

1、首先考虑到悬挂指针。悬挂指针的出现有两种可能:指针指向的对象未初始化,或者指针指向的对象已被释放。

  那么就先新建一个继承自UIView的类Square,并在ViewController中定义一个Square类的属性,正常情况下,代码应该是这样的:

格而知之4:寻找EXC_BAD_ACCESS-LMLPHP

  运行效果如下:

格而知之4:寻找EXC_BAD_ACCESS-LMLPHP

  考虑到悬挂指针的出现有两种可能:指针指向的对象未初始化;指针指向的对象已被释放。那么就分两种情况来处理:

(1)、对于第一种情况,只给这个Square类属性分配内存,不做初始化,代码与运行结果如下:

格而知之4:寻找EXC_BAD_ACCESS-LMLPHP

  代码不负所望地crash了,EXC_BAD_ACCESS。

(2)、接下试图制造第二种悬挂指针,指针指向已释放的对象。首先在Square类里重写dealloc方法,用以监听对象的释放:

格而知之4:寻找EXC_BAD_ACCESS-LMLPHP

  接着将代码修改如下:

格而知之4:寻找EXC_BAD_ACCESS-LMLPHP

  输出结果如下:

格而知之4:寻找EXC_BAD_ACCESS-LMLPHP

  可以看到对象确实已经释放了,但是却没有crash,self.s被指向了nil。在这种情况下并没有出现EXC_BAD_ACCESS,说明在当指针所指对象已被释放的情况下,ARC机制会让指针变成空指针,避免了出现悬挂指针,也就导致不了EXC_BAD_ACCESS了。

  同时,viewDidLoad的最后一句也就不会执行了。

2、我隐约记得有一次在使用UITableView的时候出现过EXC_BAD_ACCESS,似乎是在使用cellForRowAtIndexPath:去取某个没显示在页面上的cell的时候出现的,于是尝试将代码修改如下:

格而知之4:寻找EXC_BAD_ACCESS-LMLPHP

  运行后并没有出现crash,而在tableView:didSelectRowAtIndexPath:方法中有一个打印,打印的结果如下:

格而知之4:寻找EXC_BAD_ACCESS-LMLPHP

  说明这也变成了一个空指针,并不会导致EXC_BAD_ACCESS。这种情况需要以后使用UITableView的时候继续留意。

3、最后还有一种非悬挂指针导致的EXC_BAD_ACCESS的情况,即是在使用Block的时候。演示代码如下:

格而知之4:寻找EXC_BAD_ACCESS-LMLPHP

格而知之4:寻找EXC_BAD_ACCESS-LMLPHP

  可以看到,两种情况都crash了,只要Block没有值,调用的时候就会导致EXC_BAD_ACCESS。因为Block是分配在栈上不是分配在堆上的,所以并不关指针什么事,纯粹就是Block为赋值或者为空值的时候,调用它会访问到栈上错误的内容,EXC_BAD_ACCESS。

  基于此,每次使用Block的时候,都应该先判断一下是否为空。

05-08 08:20