ios中集合遍曆方法的比較和技巧

來源:互聯網
上載者:User

標籤:blog   class   code   tar   get   string   int   2014   html   set   cti   

本文原文發表自我的【自建部落格】,cnblogs同步發表,格式未經調整,內容以原部落格為準我是前言

集合的遍曆操作是開發中最常見的操作之一,從C語言經典的for迴圈到利用多核cpu的優勢進行遍曆,開發中ios有若干集合遍曆方法,本文通過研究和測試比較了各個操作方法的效率和優略勢,並總結幾個使用集合遍曆時的小技巧。

ios中常用的遍曆運算方法

遍曆的目的是擷取集合中的某個對象或執行某個操作,所以能滿足這個條件的方法都可以作為備選:

  • 經典for迴圈
  • for in (NSFastEnumeration),若不熟悉可以參考《nshipster介紹NSFastEnumeration的文章》
  • makeObjectsPerformSelector
  • kvc集合運算子
  • enumerateObjectsUsingBlock
  • enumerateObjectsWithOptions(NSEnumerationConcurrent)
  • dispatch_apply
實驗實驗條件

測試類別如下:

1234
@interface Sark : NSObject@property (nonatomic) NSInteger number;- (void)doSomethingSlow; // sleep(0.01)@end

實驗從兩個方面來評價:

  1. 分別使用有100個對象和1000000個對象的NSArray,只取對象,不執行操作,測試遍曆速度
  2. 使用有100個對象的NSArray遍曆執行doSomethingSlow方法,測試遍曆中多任務運行速度

實驗使用CFAbsoluteTimeGetCurrent()記錄時間戳記來計算已耗用時間,單位秒。
運行在iphone5真機(雙核cpu)

實驗資料

100對象遍曆操作:

1234567
經典for迴圈 - 0.001355for in (NSFastEnumeration) - 0.002308makeObjectsPerformSelector - 0.001120kvc集合運算子(@sum.number) - 0.004272 enumerateObjectsUsingBlock - 0.001145enumerateObjectsWithOptions(NSEnumerationConcurrent) - 0.001605dispatch_apply(Concurrent) - 0.001380

1000000對象遍曆操作:

1234567
經典for迴圈 - 1.246721for in (NSFastEnumeration) - 0.025955makeObjectsPerformSelector - 0.068234kvc集合運算子(@sum.number) - 21.677246enumerateObjectsUsingBlock - 0.586034enumerateObjectsWithOptions(NSEnumerationConcurrent) - 0.722548dispatch_apply(Concurrent) - 0.607100

100對象遍曆執行一個很費時的操作:

1234567
經典for迴圈 - 1.106567for in (NSFastEnumeration) - 1.102643makeObjectsPerformSelector - 1.103965kvc集合運算子(@sum.number) - N/AenumerateObjectsUsingBlock - 1.104888enumerateObjectsWithOptions(NSEnumerationConcurrent) - 0.554670dispatch_apply(Concurrent) - 0.554858
值得注意的
  • 對於集合中對象數很多的情況下,for in (NSFastEnumeration)的遍曆速度非常之快,但小規模的遍曆並不明顯(還沒普通for迴圈快)
  • 使用kvc集合運算子運算很大規模的集合時,效率明顯下降(100萬的數組離譜的21秒多),同時佔用了大量記憶體和cpu
  • enumerateObjectsWithOptions(NSEnumerationConcurrent)dispatch_apply(Concurrent)的遍曆執行可以利用到多核cpu的優勢(實驗中在雙核cpu上效率基本上x2)
遍曆實踐Tips倒序遍曆

NSArrayNSOrderedSet都支援使用reverseObjectEnumerator倒序遍曆,如:

1234
NSArray *strings = @[@"1", @"2", @"3"];for (NSString *string in [strings reverseObjectEnumerator]) {    NSLog(@"%@", string);}

這個方法只在迴圈第一次被調用,所以也不必擔心迴圈每次計算的問題。

同時,使用enumerateObjectsWithOptions:NSEnumerationReverse也可以實現倒序遍曆:

123
[array enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(Sark *sark, NSUInteger idx, BOOL *stop) {    [sark doSomething];}];
使用block同時遍曆字典key,value

block版本的字典遍曆可以同時取key和value(forin只能取key再手動取value),如:

1234
NSDictionary *dict = @{@"a": @"1", @"b": @"2"};[dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {    NSLog(@"key: %@, value: %@", key, obj);}];
對於耗時且順序無關的遍曆,使用並發版本
123
[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迴圈的寫法了。

References

http://nshipster.com/enumerators/
http://iosdevelopertips.com/objective-c/fast-enumeration-on-the-iphone.html

原創文章,轉載請註明源地址,blog.sunnyxx.com

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.