2012-02-14 75 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)];