2012-02-14 100 views
1

我有一个UIView * buttonView充满了按钮。我需要更新我的列表并重新填充我的buttonView。我实现了:为什么快速枚举在标准for循环失败时工作?

if ([[self.buttonView.subviews objectAtIndex:i] isKindOfClass:[UIButon class]] 
    [[self.buttonView.subviews objectAtIndex:i] removeFromSuperview]; 

但是失败了,它不会删除所有的按钮(与我有8次按键测试,它删除所有其他按钮: - ?)

然后我想:

for(UIView *subview in self.buttonView.subviews) 
{ 
    if([subview isKindOfClass:[UIButton class]]) 
     [subview removeFromSuperview]; 
} 

它的工作完美。

不应该两个循环完成相同的事情?

我猜想有一些我不知道快速枚举,可以解释这一点?

回答

2

我不相信你可以修改你列举的集合,所以要小心。看起来它可以很好地处理更改,并且它会通过每个项目(与for循环不同)。

在第一种情况下,它不起作用,因为一旦你删除了索引0,其他的东西就变成了索引0,但是你已经移到了索引1上。你可以通过递减循环变量来修复它,删除成功,使回路它增加其先前值:

if ([[self.buttonView.subviews objectAtIndex:i] isKindOfClass:[UIButton class]]) { 
    [[self.buttonView.subviews objectAtIndex:i] removeFromSuperview]; 
    --i; 
} 

与你发生了什么循环:

1,2,3,4,5,6,7,8并删除第一项(1)

2,3,4,5,6,7,8和删除秒OND项(3)

2,4,5,6,7,8并除去第三项(5)

2,4,6,7,8和除去第四项目(7)。

2,4,6,8而你在索引5上,这比长度大,所以你就完成了。

+0

根据实现细节,在一个循环,变异的子视图阵列可能最终被相对低效的不断调用'-subviews'。请参阅我的答案,了解可能更有效的其他解决方案。 – 2012-02-14 20:52:11

3

如果您反向运行它(如果从i > 0开始,则从最高索引和循环开始),您的for循环可能会有效。在编写for循环的方式中,每次删除对象时,索引都会更改。枚举的编写方式,可以安全地在枚举中移除对象,或者所有对象都预先返回,这不会留下任何跳过任何元素的机会。

5

你的根本问题是你在迭代时改变一个集合。如果您确实需要这样做,那么您可能需要考虑向后迭代(使用-reverseObjectEnumerator或带有反向选项的-enumerateObjectsWithOptions:usingBlock:)。或者,您可以在迭代之前创建数组的副本。

快速枚举正在工作的原因很可能是由于-subviews返回某种子视图数组的不断副本。这实际上可以在第一种情况下使用,只需调用-subviews一次并重新使用结果,但这会引入对实现细节的依赖性,我强烈建议避免它。如果你需要一个恒定的数组副本,你应该自己创建一个。


修复最快的解决办法的情况下1人:

NSArray *subviews = [NSArray arrayWithArray:self.buttonView.subviews]; 
// now enumerate using the subviews array directly instead of calling self.buttonView.subviews 

为了您快速枚举的情况下,你可能要重写,作为

for (UIView *subview in [NSArray arrayWithArray:self.buttonView.subviews]) { 
    // ... 
} 

第三种选择是开关到基于块的枚举并相反。这实际上将是最有效的选择,如果它是一个你可以做:

[self.buttonView.subviews enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(UIView *subview, NSUInteger idx, BOOL *stop) { 
    if ([subview isKindOfClass:[UIButton class]]) { 
     [subview removeFromSuperview]; 
    } 
}]; 
0

就像其他人一样说你的主要问题是修改一个可变数组在迭代它。相反,您可以找到buttonView的所有按钮,并使它们执行removeFromSuperview。

id buttonClassTest = ^(UIView * subview, NSUInteger idx, BOOL *stop){ 
    return [subview isKindOfClass:[UIButton class]]; 
}; 

NSIndexSet * indexes = [self.buttonView.subviews indexesOfObjectsPassingTest:buttonClassTest]; 
NSArray * buttons = [self.buttonView.subviews objectsAtIndexes:indexes]; 
[buttons makeObjectsPerformSelector:@selector(removeFromSuperview)];