如何在修改时安全地循环遍历可变数组?

时间:2012-09-01 19:21:10

标签: iphone objective-c ios arrays

我有一个controller,其中有一个包含actors的数组。 actor是一个将由controller调用的对象。

问题:controller遍历actors数组并向每个参与者发送-actionMessage。 actor可以使用controller创建和注册另一个actor,或者从控制器的actors数组中删除一个actor甚至自己。它通过两种方法传递:

-registerActor:(Actor*)actor;
-unregisterActor:(Actor*)actor;

因此,当控制器遍历actors数组时,actor的列表可以更改。 编辑:任何新添加的演员也必须经历循环。

处理此问题的最佳做法是什么?我应该在迭代之前创建一个actors数组的副本吗?

7 个答案:

答案 0 :(得分:7)

创建可变数组的副本并迭代它。

NSArray *loopArray = [NSArray arrayWithArray: yourActorArray];

或者

NSArray *loopArray = [yourActorArray copy];
//in this case remember to release in nonARC environment

答案 1 :(得分:2)

这就是我通常做的......

NSMutableArray *discardedItems = [NSMutableArray array];
SomeObjectClass *item;

for (item in originalArrayOfItems) {
    if ([item shouldBeDiscarded])
        [discardedItems addObject:item];
}

[originalArrayOfItems removeObjectsInArray:discardedItems];

希望这会有所帮助。

答案 2 :(得分:2)

通过一个可变数组进行枚举的标准过程需要在逐步执行时进行更改,这是为了复制并迭代它,改变原始数据。我猜测演员的NSMutableArray是属于你的控制器的属性,而registerActor:和unregisterActor:都改变了这个数组。如果您单步执行副本,则可以通过方法从原始属性中删除actor,而无需更改副本。

NSMutableArray *actorArrayCopy = [self.actorArray copy];
for (id object in actorArrayCopy){
    //do stuff
}        

在某些情况下,您可以废弃快速枚举并使用标准for循环,但是,这在这里存在风险。如果从数组中插入或删除对象,索引将移位(AFAIK),这意味着您可能最终跳过元素或多次遍历元素。

有些人将要更改的元素(例如已删除)存储在快速枚举中的单独数组中,并在枚举完成后立即执行所有更改,但是您使用单独的方法来添加和删除元素;元素本身决定了应该发生什么并通知你。这会使事情变得更复杂,因此副本可能效果最好。

答案 3 :(得分:1)

您可以通过执行以下操作来避免复制数组:

for(int i=0; i<[array count]; i++)
{
    if(condition)
    {
        [array removeObjectAtIndex:i];

        i --;
        continue;
    }
}

答案 4 :(得分:1)

您应该使用集合而不是数组。然后你可以复制这个集合,并在完成操作后,取一个差异来看看发生了什么变化。

答案 5 :(得分:0)

我创建了一个名为smartFor的宏,该宏可以让您进行与for循环快速枚举相同的设置,但可以处理数组的修改。它利用了一些C和CLANG漏洞,非常简单干净。


宏:

///Allows for modifying an array during virtual fast enumeration
#define smartFor(__objectDeclaration, __array, __block) {\
int __i = 0;\
while (__i < __array.count) {\
__objectDeclaration = __array[__i];\
NSObject *__object = __array[__i];\
[__block invoke];\
if ([__array indexOfObjectIdenticalTo:__object] != NSNotFound && ((int)[__array indexOfObjectIdenticalTo:__object]) <= __i) {\
__i = ((int)[__array indexOfObjectIdenticalTo:__object])+1;\
}\
}\
}

它会自动处理您可能要做的所有[基本]不可思议的事情,例如添加对象(在数组中的任何位置,在“快速枚举”中的任何时候 [1] ),删除对象,调整对象的索引,修改对象等。


用法示例:

smartFor(NSNumber *aNumber, myArray, ^{
    if ([aNumber isEqualToNumber:@(3)]) {
        [myArray removeObjectIdenticalTo:aNumber];
    }
});

[1] 注意:显然,在严格的定义/性能基础上,这并不是技术上的快速枚举,但是出于所有目的和目的,其行为都与之类似

答案 6 :(得分:-2)

你应该使用普通的for循环

for ( ''initializer''; ''conditional expression''; ''loop expression'' )
{
  // statements to be executed
}

不要使用fast / objective -c进行循环。它允许你编辑数组......