这两天发现app的一个崩溃,提示是
*** Collection <CALayerArray: 0x18904630> was mutated while being enumerated.
找了一天终于找到了罪魁祸首,代码是这样的
for (NSInteger i = 0; i < postArr.count; i++) {
for (NSInteger j = i+1; j < postArr.count; j++) {
MoviePhoto *mp = postArr[i];
MoviePhoto *secMp =postArr[j];
if ([mp.photoUrl.value isEqualToString:secMp.photoUrl.value]) {
[postArr removeObject:mp];//删除重复对象
}
}
}
原因是这样的,for循环在编译的时候可能就不会改变被遍历数组的数据,当进入if语句数组进行remove了之后,再去遍历数组,发现被遍历数组和编译的时候不一样。此时就会造成崩溃。
在使用数组的时候,我们应该避免遍历数组时不要对数组进行操作。但是对于我们有时候要使用到对用一个数组进行遍历又要进行修改的时候,网上提供了两种方法:
1遍历数组A操作数组B;
NSMutableArray
* arrayTemp = xxx;
NSArray
* array = [
NSArray
arrayWithArray: arrayTemp];
for
(
NSDictionary
* dic in array) {
if
(condition){
[arrayTemp removeObject:dic];
}
}
2.找到符合的条件之后,暂停遍历,然后修改数组的内容
NSMutableArray
*tempArray = [[
NSMutableArray
alloc]initWithObjects:
@"12"
,
@"23"
,
@"34"
,
@"45"
,
@"56"
,
nil
];
[tempArray enumerateObjectsUsingBlock:^(
id
obj,
NSUInteger
idx,
BOOL
*stop) {
if
([obj isEqualToString:
@"34"
]) {
*stop =
YES
;
if
(*stop ==
YES
) {
[tempArray replaceObjectAtIndex:idx withObject:
@"3333333"
];
}
}
if
(*stop) {
NSLog
(
@"array is %@"
,tempArray);
}
}];
PS:利用block来操作,根据查阅资料,发现block便利比for便利快20%左右;
如果都不是数组遍历的原因,有可能是线程的原因。这里贴上了网上的一些资料:
遇到一个问题,跟踪了半天才发现原因。(现象是,客户老是说在下载的过程中,过一会就出现崩溃的现象, 只要点了下载按钮, 不做任何操作, 然后期刊就开始正常下载了,过大概5分钟后,再去看,程序就已经崩溃了)
在做《时装传媒》ipad应用时,首页的下载页面,下载时,会不断地去更新页面上的下载进度,同时还有一个15秒的timer, 会每15秒切换当前显示的是男装,女装或者艺术刊页面。这个timer会把当前的scrollView中显示的封面图片进行移除,然后再替换,以实现自动更换首页封面的功能
因为我们在下载时, 使用了Block每当接受到数据,就去更新一下一个UIScrollView中的下载进度条的百分比。
而另有一个NSTimer, 会隔一段时间把这个UIScrollView中的全部元素清空,然后再重新添加, 因为可能有动态的元素需要添加或者删除。
运行时, 就会出现,两个线程同时去访问的情况, 当Block正在去更新UIScrollView中的这个元素时, NSTimer中可能正在执行清除操作,所以就崩了。
原来,在同一时间,不同的线程同时读取和修改了NSMutaleArray。
解决办法:避免多线程同时去修改一个对象, 避免做遍历时,这个对象被修改,根据这个思想,可以考虑加锁,或者直接使用atomic的方式来做。