问题

在开发过程中对于 NSArray 和 NSMutableArray 等的使用不当,出现crash,在最近的开发中对于枚举 sublayers 操作遇到这样的错误。

1
Collection <__NSArrayM: 0xxxxxxx> was mutated while being enumerated.

字面意思就是--集合(数组)在枚举时发生突变。
同时对可变数组进行两种操作:枚举和内容修改(添加或者删除)。对于枚举器而言,容易已经改变了,很可能访问到未分配的内存空间。所以这样做是很危险的。

解决

在网上找了下这样的问题,自己也实践了下,发现要对操作的数组进行一次mutableCopy。

1
2
3
4
5
NSMutableArray *mutableArray = [[[NSMutableArray alloc] initWithObjects:@"1",@"2",@"3",@"4", nil] mutableCopy];
//NSMutableArray *mArray = mutableArray;
[mutableArray enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[mutableArray removeObject:obj];
}];

解决这里还有个疑惑 UIView对象中的subviews成员,子视图removeFromSuperview时为什么不会有上述的问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
UIView *v1 = [[UIView alloc] init];
UIView *v2 = [[UIView alloc] init];
UIView *v3 = [[UIView alloc] init];
UIView *v4 = [[UIView alloc] init];

[v1 addSubview:v2];
[v1 addSubview:v3];
[v1 addSubview:v4];

NSLog(@"修改之前 - %@",v1.subviews);

for (UIView *v in v1.subviews) {
if(v == v3) {
[v removeFromSuperview];
}
}

NSLog(@"修改之后 - %@",v1.subviews);

以上述代码为例,其实修改前的v1.subviews对象和修改后的对象不是一个对象了,简单的说就是修改前后v1.subviews对象地址发生变化

修改前

1
2
3
4
5
6
Printing description of v1->_subviewCache:
<__NSArrayM 0x7a07f480>(
<UIView: 0x79e79c90; frame = (0 0; 0 0); layer = <CALayer: 0x79e79b30>>,
<UIView: 0x79e7aeb0; frame = (0 0; 0 0); layer = <CALayer: 0x79e7fd60>>,
<UIView: 0x79e7e660; frame = (0 0; 0 0); layer = <CALayer: 0x79e78b60>>
)

修改后

1
2
3
4
5
Printing description of v1->_subviewCache:
<__NSArrayM 0x7a0af2d0>(
<UIView: 0x79e79c90; frame = (0 0; 0 0); layer = <CALayer: 0x79e79b30>>,
<UIView: 0x79e7e660; frame = (0 0; 0 0); layer = <CALayer: 0x79e78b60>>
)

到此问题解决了。虽然是一个小的问题。