网易面试题引发的思考

题目:下面代码会发生什么问题?
@property (nonatomic, strong) NSString *target;
//....
dispatch_queue_t queue = dispatch_queue_create("parallel", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 1000000 ; i++) {
    dispatch_async(queue, ^{
        self.target = [NSString stringWithFormat:@"ksddkjalkjd%d",i];
    });
}
这段代码,看上去没有什么问题,运行一下,发现奔溃,报错EXC_BAD_ACCESS
网易面试题引发的思考
网易面试题引发的思考
这样看来是对已经释放的对方再次发送了release消息。
回头看这代码,dispatch_async是异步调用,也就是说在这个循环中,前一个异步调用执行的语句还是没有完成,后一个已经开始在执行了,代码块中的程序执行相互是不受影响的。但是问题在于执行的是同一段代码,我们在来看target是用strong来修饰的,set方式实际执行了什么?
- (void)setTarget:(NSString *)target {
    [target retain];//先保留新值
    [_target release];//再释放旧值
    _target = target;//再进行赋值
}
这样看来,前一个异步调用执行到将要执行第三步,后一个再次开始执行第二步relase,这样新的target还没有被赋值,所以_target本来就处于被释放的状态,再次relase就奔溃了。

通过这个案例我们发现循环的异步调用某一个方法时,需要考虑改方法的实际情况,防止出现EXC_BAD_ACCESS的情况,我们在大多数时候都是直接使用ARC,没有关注到内存释放、对象消亡的过程。一旦出现EXC_BAD_ACCESS,多半都是因为你提前释放了某个对象,然后又在对该对象进行操作。

这样的话,我们提供一种调试方法,来帮助准确定位问题所在,Xcode开启僵尸模式
选中编译项目点击Edit Scheme->选中diahnostics->勾选Zombie Object(僵尸对象选项),如图
网易面试题引发的思考
网易面试题引发的思考
网易面试题引发的思考
网易面试题引发的思考
这样的话,如果项目中存在僵尸对象,控制台会打印出具体的问题,准确定位到被提前释放的对象。