集合的遍歷操做是開發中最多見的操做之一,從C語言經典的for循環到利用多核cpu的優點進行遍歷,開發中ios有若干集合遍歷方法,本文經過研究和測試比較了各個操做方法的效率和優略勢,並總結幾個使用集合遍歷時的小技巧。html
遍歷的目的是獲取集合中的某個對象或執行某個操做,因此能知足這個條件的方法均可以做爲備選:ios
測試類以下:objective-c
1 2 3 4 |
@interface Sark : NSObject @property (nonatomic) NSInteger number; - (void)doSomethingSlow; // sleep(0.01) @end |
實驗從兩個方面來評價:數組
doSomethingSlow
方法,測試遍歷中多任務運行速度實驗使用CFAbsoluteTimeGetCurrent()
記錄時間戳來計算運行時間,單位秒。
運行在iphone5真機(雙核cpu)併發
100對象遍歷操做:app
1 2 3 4 5 6 7 |
經典for循環 - 0.001355 for in (NSFastEnumeration) - 0.002308 makeObjectsPerformSelector - 0.001120 kvc集合運算符(@sum.number) - 0.004272 enumerateObjectsUsingBlock - 0.001145 enumerateObjectsWithOptions(NSEnumerationConcurrent) - 0.001605 dispatch_apply(Concurrent) - 0.001380 |
1000000對象遍歷操做:iphone
1 2 3 4 5 6 7 |
經典for循環 - 1.246721 for in (NSFastEnumeration) - 0.025955 makeObjectsPerformSelector - 0.068234 kvc集合運算符(@sum.number) - 21.677246 enumerateObjectsUsingBlock - 0.586034 enumerateObjectsWithOptions(NSEnumerationConcurrent) - 0.722548 dispatch_apply(Concurrent) - 0.607100 |
100對象遍歷執行一個很費時的操做:測試
1 2 3 4 5 6 7 |
經典for循環 - 1.106567 for in (NSFastEnumeration) - 1.102643 makeObjectsPerformSelector - 1.103965 kvc集合運算符(@sum.number) - N/A enumerateObjectsUsingBlock - 1.104888 enumerateObjectsWithOptions(NSEnumerationConcurrent) - 0.554670 dispatch_apply(Concurrent) - 0.554858 |
for in (NSFastEnumeration)
的遍歷速度很是之快,但小規模的遍歷並不明顯(還沒普通for循環快)kvc集合運算符
運算很大規模的集合時,效率明顯降低(100萬的數組離譜的21秒多),同時佔用了大量內存和cpuenumerateObjectsWithOptions(NSEnumerationConcurrent)
和dispatch_apply(Concurrent)
的遍歷執行能夠利用到多核cpu的優點(實驗中在雙核cpu上效率基本上x2)NSArray
和NSOrderedSet
都支持使用reverseObjectEnumerator
倒序遍歷,如:ui
1 2 3 4 |
NSArray *strings = @[@"1", @"2", @"3"]; for (NSString *string in [strings reverseObjectEnumerator]) { NSLog(@"%@", string); } |
這個方法只在循環第一次被調用,因此也沒必要擔憂循環每次計算的問題。atom
同時,使用enumerateObjectsWithOptions:NSEnumerationReverse
也能夠實現倒序遍歷:
1 2 3 |
[array enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(Sark *sark, NSUInteger idx, BOOL *stop) { [sark doSomething]; }]; |
block版本的字典遍歷能夠同時取key和value(forin只能取key再手動取value),如:
1 2 3 4 |
NSDictionary *dict = @{@"a": @"1", @"b": @"2"}; [dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { NSLog(@"key: %@, value: %@", key, obj); }]; |
1 2 3 |
[array enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(Sark *sark, NSUInteger idx, BOOL *stop) { [sark doSomethingSlow]; }]; |
遍歷執行block會分配在多核cpu上執行(底層極可能就是gcd的併發queue),對於耗時的任務來講是很值得這麼作的,並且在之後cpu升級成更多核心後不用改代碼也能夠享受帶來的好處。同時,對於遍歷的外部是保持同步的(遍歷都完成後才繼續執行下一行),猜測內部大概是gcd的dispatch_group或者信號量控制。
雖說上面的測試結果代表,在集合內元素很少時,經典for循環的效率要比forin要高,可是從代碼可讀性上來看,就遠不如forin看着更順暢;一樣的還有kvc的集合運算符,一些內置的操做以keypath
的方式聲明,相比本身用for循環實現,一行代碼就能搞定,清楚明瞭,還省去了重複工做;在framework中增長了集合遍歷的block支持後,對於須要index的遍歷不再須要經典for循環的寫法了。
http://nshipster.com/enumerators/
http://iosdevelopertips.com/objective-c/fast-enumeration-on-the-iphone.html
原創文章,轉載請註明源地址,blog.sunnyxx.com