天天看點

iOS開發Tips3:循環的性能

周遊清單應該是平時開發中最常見的一種操作了。說起周遊效率,相比于其他方面的因素(可維護性,易懂等),也許周遊的效率在你的代碼中并不是那麼重要。

但我們還是要了解一下從性能方面去考慮周遊這個問題,以便在一寫特殊的case下知道怎麼着手去優化。

下面是幾種常用的周遊方式:

1.  for (NSInteger i = 0 ; i < len; i++)

2. for ( NSObject * o bj in  Array)

3. - (void)enumerateObjectsUsingBlock:(void (NS_NOESCAPE ^)(ObjectType obj, NSUInteger idx, BOOL *stop))block 

4.  dispatch_apply( size_t iterations, dispatch_queue_t queue,

DISPATCH_NOESCAPE void (^block)(size_t));

5.NSEnumerator

1. 我們最常用的周遊方式

2. 快速周遊,這種寫法在 寫法上更直接明了,可以直接拿到數組中的元素

3. 蘋果提供的block塊周遊, 有以下特點:

    1. 索引和元素均可以傳回,其他隻傳回元素

    2. 并發枚舉,也不是說并發就一定高效,當有大量的任務去做的時候,在多核處理器下,對性能的提升比較明顯,相反,要是任務很小,它帶來的好處可能并不大,在性能比較重要的場景可以試驗權衡。

4. Index 順序不确定,因為它是并行執行的(dispatch_get_global_queue是并行隊列)。這裡 dispatch_apply如果換成串行隊列上,則會依次輸出index。

dispathc_apply 是dispatch_sync 和dispatch_group的關聯API.它以指定的次數将指定的Block加入到指定的隊列中。并等待隊列中操作全部完成。

5. 每次被調用時會生成NSEnumerator執行個體,并指向第一個元素,和連結清單next指針一樣,nextObject方法傳回下一個元素,直到元素為nil止。

下面看代碼,我們目标是擷取到每個元素:

- (void)loop_test
{
    NSArray *sampleItemArray = [self itemArray];
    CFTimeInterval startTime = CFAbsoluteTimeGetCurrent();
   
    //  - - - - - - - - - - - --- - - -- - -- -- - - - -- - -- -

    NSInteger len = [sampleItemArray count];
    
    for (NSInteger i = 0 ; i < len; i++){
        NSObject *obj = sampleItemArray[i];

    }
    NSLog(@"common for-in loop: %.8f", CFAbsoluteTimeGetCurrent() - startTime);
    
    
//  - - - - - - - - - - - --- - - -- - -- -- - - - -- - -- -
    startTime = CFAbsoluteTimeGetCurrent();
    for (NSObject *obj in sampleItemArray){
        
    }
    NSLog(@"fast for-in loop: %.8f", CFAbsoluteTimeGetCurrent() - startTime);
    
//  - - - - - - - - - - - --- - - -- - -- -- - - - -- - -- -

    startTime = CFAbsoluteTimeGetCurrent();
    [sampleItemArray enumerateObjectsUsingBlock:^(NSObject * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        
    }];
    NSLog(@"enumerate-block loop: %.8f", CFAbsoluteTimeGetCurrent() - startTime);

//  - - - - - - - - - - - --- - - -- - -- -- - - - -- - -- -

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    startTime = CFAbsoluteTimeGetCurrent();
    dispatch_apply([sampleItemArray count], queue, ^(size_t index) {
        NSObject *obj = sampleItemArray[index];
        
    });
    NSLog(@"dispatch_apply loop: %.8f", CFAbsoluteTimeGetCurrent() - startTime);

//  - - - - - - - - - - - --- - - -- - -- -- - - - -- - -- -

    NSObject *obj = nil;
    
    startTime = CFAbsoluteTimeGetCurrent();
    NSEnumerator *enumerator = [sampleItemArray objectEnumerator];
    while ((obj = [enumerator nextObject])) {
    
    }
    NSLog(@"NSEnumerator loop: %.8f", CFAbsoluteTimeGetCurrent() - startTime);
}
           

列印結果:

common for loop:      0.00076401

fast for-in loop:     0.00004494

enumerate-block loop: 0.00093901

dispatch_apply loop:  0.00052500

NSEnumerator loop:    0.00072098

結論分析:資料來看 1. 性能最優 的是快速枚舉for-in

2. enumerate-block性能最差,是快速枚舉的近20倍。

3. NSEnumerator和普通的for循環差别不大,是apply的1.4倍