如何在枚举过程中从NSMutableArray或NSMutableDictionary中删除元素?

12

我正在使用类似于以下代码的块枚举:

[[[rows objectForKey:self.company.coaTypeCode] objectForKey:statementType] 
    enumerateObjectsWithOptions:NSEnumerationConcurrent 
                     usingBlock:^(id coaItem, NSUInteger idx, BOOL *stop) { 
// block code here
}]

我希望在枚举过程中根据对象值删除一些对象。

我该如何实现呢?我知道在枚举期间操作可变数组或字典(NSMutableArray或NSMutableDictionary)通常是不可能的。

最好的实现方式是什么?

谢谢!

3个回答

39

由于在枚举期间无法从数组或字典中删除对象,因此您必须积累要删除的项目,然后在枚举之后全部删除。

如果您正在处理数组,则可以只积累索引:

NSMutableIndexSet *indexesToDelete = [NSMutableIndexSet indexSet];
NSUInteger currentIndex = 0;

for (id obj in yourArray) {
    //do stuff with obj
    if (shouldBeDeleted(obj)) {
        [indexesToDelete addIndex:currentIndex];
    }
    currentIndex++;
}

[yourArray removeObjectsAtIndexes:indexesToDelete];

由于NSDictionary中键的顺序是未定义的,因此对于NSMutableDictionary,您必须累积键:

NSMutableArray *keysToDelete = [NSMutableArray array];

for (id obj in [yourDictionary keyEnumerator]) {
    //do stuff with obj
    if (shouldBeDeleted(obj)) {
        [keysToDelete addObject:obj];
    }
}

[yourDictionary removeObjectsForKeys:keysToDelete];

如果您正在使用块进行枚举,那么情况就相同。在声明块的同一作用域中声明枚举器,它将被保留并正常工作。

还值得关注3年前的这个问题:在遍历时从NSMutableArray中删除的最佳方法是什么?


8
无论您在枚举过程中是否建立索引集,或者在枚举过程中修改数组本身,都必须放弃NSEnumerationConcurrent,因为大多数Cocoa对象不能同时从多个线程安全地进行修改。
无论如何,最简单(但可能不是最有效)的方法是仅枚举容器的副本。
对于数组,您可以反向枚举一个副本。我假设在枚举每个项时,您可能会决定删除该项,但不会删除先前枚举或尚未枚举的其他项。
NSMutableArray *array = [[rows objectForKey:self.company.coaTypeCode] objectForKey:statementType];
[[array copy] enumerateObjectsWithOptions: NSEnumerationReverse 
    usingBlock:^(id coaItem, NSUInteger idx, BOOL *stop) {
    if ([self objectIsTooUglyToExist:coaItem])
        [array removeObjectAtIndex:idx];
}]

为避免更改尚未枚举的数组部分,您需要以相反的顺序枚举数组。

对于字典,您可以只枚举一个没有特殊选项的副本:

NSMutableDictionary *dictionary = someDictionary;
[[dictionary copy] enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
    if ([self object:obj isTooUglyToExistAtKey:key])
        [dictionary removeObjectForKey:key];
}];

感谢你提供的“NSEnumerationConcurrent”选项提示,Rob! - AlexR

1

另一种选项是使用传统的for循环和数组的count作为限制。然后需要注意的是,如果从位置<=删除元素(在这种情况下,索引应该递减),或者>索引(在这种情况下,索引除了for语句的增量之外不会被修改)。

对于字典,您可以首先使用allKeys创建一个数组,然后遍历该数组。在这种情况下,不需要调整索引值。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接