为什么在循环标准失败时快速枚举工作?

Pad*_*215 1 cocoa-touch objective-c ios

我有一个充满按钮的UIView*buttonView.我需要更新我的列表并重新填充我的buttonView.我实施了:

if ([[self.buttonView.subviews objectAtIndex:i] isKindOfClass:[UIButon class]]
    [[self.buttonView.subviews objectAtIndex:i] removeFromSuperview];
Run Code Online (Sandbox Code Playgroud)

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

然后我尝试了:

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

它工作得很好.

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

我猜有些东西我不知道快速枚举可以解释这个吗?

Lil*_*ard 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
Run Code Online (Sandbox Code Playgroud)

对于快速枚举的情况,您可能希望将其重写为

for (UIView *subview in [NSArray arrayWithArray:self.buttonView.subviews]) {
    // ...
}
Run Code Online (Sandbox Code Playgroud)

第三种选择是切换到基于块的枚举并反向进行.如果你能做到这一点,这实际上将是最有效的选择:

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