我有一个NSMutableArray
UIImageViews
我会不断迭代(使用CADisplayLink
)。我这样做是为了在屏幕上不断移动UIImageViews
。
当我从数组中删除UIImageView
时,我有时会收到错误:
EXC_BAD_ACCESS (code=1, address=0x20000008)
然而,在我开始使用ARC / iOS6之前,我过去常常使用错误“阵列在被枚举时被突变”,所以我认为这两个错误意味着完全相同但我不确定。
无论如何,这是我的问题。使用以下任何一种方法从数组中删除UIImageView
会给出上述错误。
[imageViews removeObject:imageView];
[imageViews performSelector:@selector(removeObject:) withObject:imageView];
[imageViews performSelectorOnMainThread:@selector(removeObject:) withObject:imageView waitUntilDone:YES];
但是,使用以下任何方法从不会给我错误。
[imageViews performSelectorOnMainThread:@selector(removeObject:) withObject:imageView waitUntilDone:NO];
[imageViews performSelector:@selector(removeObject:) withObject:imageView afterDelay:.01];
所以有人可以向我解释为什么使用上面列出的第一组方法从NSMutableArray
中删除对象给出了我认为“在枚举时数组被突变”的错误,同时使用第二组方法从来没有给我这个错误?
我一直在使用上面列出的第二组方法来解决这个错误(如果我使用它们,一切都很完美)但是我想确切地知道为什么我的问题在我使用时解决了使用这两种方法中的一种是安全的。
这是我第一次发帖提问,如果我以错误的方式发帖,请原谅我。
答案 0 :(得分:2)
因为CADisplayLink回调后台线程,所以完全有可能通过调度有问题的数组从所述线程中移除给定对象,另一个正在变异或枚举数组,这会抛出异常。前两个方法不起作用,因为它们需要对数组的同步访问(NSMutableArray不提供),并且它们在当前线程的运行循环中而不是主线程中进行调度,因此无法保证它们各自的执行不能同时访问同一资源。底部的两个工作是因为,假设你在主线程上改变了数组,那个运行循环不可能安排指令枚举和同时变异的指令(一个线程不能运行两个代码)同时,特别是当在运行循环的下一次迭代中调度该代码时),因此该数组访问更安全。通过使用延迟,您可以创建一个竞争条件,这对于您迄今为止已经工作过,但会在以后再次困扰您。
答案 1 :(得分:1)
当您通过数组枚举(使用for ... in ...循环或使用NSEnumerator逐步执行它)时,枚举要求您不要在循环本身或同时在另一个线程上更改数组(因此最初的警告)。
我不知道为什么底部的两个方法都有效 - 我内部猜测它们会以不影响枚举的方式改变数组。
话虽如此,这是不好的做法,我不建议以任何方式在枚举期间更改数组。即使它今天有效,也可能不会明天。我建议存储要在枚举之后删除的索引,然后将其删除,如this answer中所示。