以forin的方式遍歷數組時進行刪除操作的注意點

NO IMAGE

今天在修改某項需求的時候,需要在遍歷的時候將匹配項移除掉,採用的時forin的方式遍歷,然後運行的時候卻crash掉了

for (NSString*str in self.btnArray) {
        if ([imageName isEqualToString:str]) {
            [self.btnArray removeObject:str];
        }
    }

於是我換了常規的方法來遍歷數組,同樣的也是在匹配的時候將匹配項移除掉,然後運行,沒有crash,能完美處理

for(int i = 0; i < self.btnArray.count; i++) {
        if ([imageName isEqualToString:self.btnArray[i]]) {
            [self.btnArray removeObject:self.btnArray[i]];
        }
    }

然後我就想,到底什麼原因呢?於是我在apple官方文檔查了下快速遍歷的利弊,發現這麼一段話:

https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSEnumerator_Class/index.html

You send nextObject repeatedly to a newly created NSEnumerator object to have it return the next object in the original collection. When the collection is exhausted, nil is returned. You cannot “reset” an enumerator after it has exhausted its collection. To enumerate a collection again, you need a new enumerator.

The enumerator subclasses used by NSArray, NSDictionary, and NSSet retain the collection during enumeration. When the enumeration is exhausted, the collection is released.

#NOTE

It is not safe to modify a mutable collection while enumerating through it. Some enumerators may currently allow enumeration of a collection that is modified, but this behavior is not guaranteed to be supported in the future.

大概的意思是說,快速遍歷的原理是根據enumerator對象內部的計數器,調用nextObject方法來實現返回下一個數組元素的,直到元素全部返回就會返回nil,於是整個enumerator對象就遍歷完了;同時也提醒,以這種原理來遍歷enumerator對象的話,無論對這個對象做什麼操作,對象的計數器都不會被重置!

注意下面的NOTE,建議最好不要再快速遍歷的時候修改enumerator,否則不保證是安全的.

由此就明白了,可能是我們在快速遍歷的時候,移除掉一個元素,但是計數器依舊是原來的,那麼在遍歷到最後會繼續調用nextObject方法,而此時實際上已經全部遍歷完了,但是系統並不知道,還在遍歷,也就是越界;當發現沒有元素時,就crash了;而我寫的常規遍歷法為什麼就可以呢,那是因為i < self.btnArray.count,這個判斷條件中,當數組元素個數變化時,self.btnArray.count也在變,就不會出現數組越界的情況,因此第二種方法是可行的;

但是我還是想用第一種快速遍歷的方法,因為我不需要按照順序遍歷,而且我希望能以最快的速度遍歷,那麼有沒有辦法解決呢?最後我想當一個辦法,既然在遍歷的時候移除元素後,計數器沒有變,為了防止數組越界,那麼我是否可以直接終止掉遍歷呢?這樣不就不會crash了嗎?

for (NSString*str in self.btnArray) {
if ([imageName isEqualToString:str]) {
[self.btnArray removeObject:str];
break;
}
}

我在移除元素後,加一個break,結束當前的遍歷,運行後,無問題!

另外我在網上看到有網友說可以逆序遍歷,再刪除,同樣不會crash,這種貌似只適用於刪除的元素在可變數組中是唯一存在的情況,當刪除的元素在可變數組中存在多個相同的時候,同樣會crash;並且官方文檔並不建議這麼做:

When you use this method with mutable subclasses of NSArray, you must not modify the array during enumeration.

我提出的這個方案,只能刪除數組中的一個元素,如果要刪除多個的話,可以參考:
Zhangzhan_zg的博客:遍歷可變數組的同時刪除數組元素的幾種解決方案

相關文章

使用Autolayout對多行文本Label進行佈局,高度不準確的解決辦法!

使用MLeaksFinder檢測項目內存洩露總結

Operation搭建個人博客

Inordertovalidateadomainnameforselfsignedcertificates,youMUSTusep