大家在实际应用中会遇到这种场景,有一个可变数组,需要删除某些特定的元素,一般情况下自然会想到遍历删除,接下来就给大家讲解一下这几种情况会发生的问题.
第一种:(for in)
NSMutableArray *array = [NSMutableArray arrayWithObjects:@"1",@"2",@"3",@"4", nil];
for (NSString *string in array) {
[array removeObject:array[1]];
}
NSLog(@"%@",array);
*** Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSArrayM: 0x60400044fc00> was mutated while being enumerated.'
这里的结果直接是crash,原因是什么呢?
因为for in 的原理是根据enumerator对象内部的计数器,调用
[nextObject]
方法来实现返回下一个数组元素的,直到元素全部返回就会返回nil,于是整个enumerator对象就遍历结束,这种原理来遍历enumerator对象的话,无论对这个对象做什么操作,对象的计数器都不会被重置.
所以当我们使用for in 移除掉一个元素,但是计数器依旧是原来的,那么在遍历到最后会继续调用nextObject
方法,而此时实际上已经全部遍历完了,但是系统并不知道,还在遍历,也就是越界;当发现没有元素时,就crash了
第二种:(for)
NSMutableArray *array = [NSMutableArray arrayWithObjects:@"1",@"2",@"3",@"4", nil];
for (int i = 0; i < array.count; i++) {
[array removeObject:array[1]];
}
NSLog(@"%@",array);
(
1,
4
)
这里的结果并没有crash,但是为什么删除了一个下标元素,而最后只剩下两个元素呢?
这里造成的原因是因为每次读取
array.count
的时候count
就变了,当删除第一个下标为1的元素的时候 array数组里只剩下[@"1",@"3",@"4"]
了,然后进行下一次遍历的时候元素3的下标就是1了,所以又把3给删除了,最后只剩下1和4了
第三种:(enumerate)
NSMutableArray *array = [NSMutableArray arrayWithObjects:@"1",@"2",@"3",@"4", nil];
[array enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[array removeObject:array[1]];
}];
NSLog(@"%@",array);
(
1,
4
)
这里的结果和上面for的结果一样
从这里可以总结得知:for in遍历过程中进行删除操作就会crash,而for和enumerate不会crash,但是会造成数据混乱,多删的现象,所以大家要记住在实际项目中注意这点.
上面这些是根据数组下标删除的,还有一种是直接删除元素,来继续看一下
for in
NSMutableArray *array = [NSMutableArray arrayWithObjects:@"1",@"2",@"3",@"4", nil];
for (NSString *string in array) {
if ([string isEqualToString:@"2"]) {
[array removeObject:string];
}
}
NSLog(@"%@",array);
结果:*** Terminating app due to uncaught exception 'NSGenericException',
reason: '*** Collection <__NSArrayM: 0x60400044a380> was mutated while being enumerated.'
for
NSMutableArray *array = [NSMutableArray arrayWithObjects:@"1",@"2",@"3",@"4", nil];
for (int i = 0; i < array.count; i++) {
if ([array[i] isEqualToString:@"2"]) {
[array removeObject:array[i]];
}
}
NSLog(@"%@",array);
结果:(
1,
3,
4
)
enumerate
NSMutableArray *array = [NSMutableArray arrayWithObjects:@"1",@"2",@"3",@"4", nil];
[array enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([obj isEqualToString:@"2"]) {
[array removeObject:obj];
}
}];
NSLog(@"%@",array);
结果:(
1,
3,
4
)
结论: 如果根据判断指定元素去删除的话,for和enumerate不会crash,也不会造成数据混乱,多删的现象,而for in的话照样是crash
最后出一道题供大家思考一下,为什么会crash
NSMutableArray *array = [NSMutableArray arrayWithObjects:@"1",@"2",@"3",@"4",@"1", nil];
for (int i = array.count - 1; i >= 0; i--) {
if ([array[i] isEqualToString:@"1"]) {
[array removeObject:array[i]];
}
}