做app的时候,总免不了要多次遍历数组或者字典。
究竟哪种遍历方式比较快呢?我做了如下测试:
首先定义测试用宏:
#define MULogTimeintervalBegin(INFO) NSTimeInterval start = [NSDate timeIntervalSinceReferenceDate];\
NSTimeInterval duration = 0;\
NSLog(@"MULogTimeintervalBegin:%@", INFO)
#define MULogTimeintervalPauseAndLog(INFO) duration = [NSDate timeIntervalSinceReferenceDate] - start;\
start += duration;\
NSLog(@"%@:%f", INFO, duration);\
duration = 0
#define TESTSCALE 100000
接着编写测试代码:
NSarray:
- (void)testArray
{
NSMutableArray* testArray = [NSMutableArray arrayWithCapacity:TESTSCALE];
for (NSInteger i = 1; i <= TESTSCALE; ++i) {
[testArray addObject:[NSString stringWithFormat:@"%ld", i]];
}
NSLog(@"init:%ld", [testArray count]);
__block NSMutableString* sum = [NSMutableString stringWithCapacity:TESTSCALE];
MULogTimeintervalBegin(@"ArrayTest");
NSUInteger count = [testArray count];
for (NSInteger i = 0; i < count; ++i) {
[sum appendString:[testArray objectAtIndex:i]];
}
[sum setString:@""];
MULogTimeintervalPauseAndLog(@"for statement");
for(NSString* item in testArray) {
[sum appendString:item];
}
[sum setString:@""];
MULogTimeintervalPauseAndLog(@"for-in");
[testArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[sum appendString:obj];
}];
[sum setString:@""];
MULogTimeintervalPauseAndLog(@"enumerateBlock");
}
- (void)testDictionary {
NSMutableDictionary* testDic = [NSMutableDictionary dictionaryWithCapacity:TESTSCALE];
for (NSInteger i = 1; i <= TESTSCALE; ++i) {
[testDic setObject:@"test" forKey:[NSString stringWithFormat:@"%ld", i]];
}
NSLog(@"init:%ld", [testDic count]);
__block NSMutableString* sum = [NSMutableString stringWithCapacity:TESTSCALE];
MULogTimeintervalBegin(@"DictionaryTest");
for (NSString* object in [testDic allValues]) {
[sum appendString:object];
}
[sum setString:@""];
MULogTimeintervalPauseAndLog(@"for statement allValues");
for (id akey in [testDic allKeys]) {
[sum appendString:[testDic objectForKey:akey]];
}
[sum setString:@""];
MULogTimeintervalPauseAndLog(@"for statement allKeys");
[testDic enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
[sum appendString:obj];
} ];
MULogTimeintervalPauseAndLog(@"enumeration");
}
Test Case '-[LoopTestTests testArray]' started.
2012-08-02 17:14:22.061 otest[388:303] init:100000
2012-08-02 17:14:22.062 otest[388:303] MULogTimeintervalBegin:ArrayTest
2012-08-02 17:14:22.075 otest[388:303]for statement:0.013108
2012-08-02 17:14:22.083 otest[388:303]for-in:0.008186
2012-08-02 17:14:22.095 otest[388:303]enumerateBlock:0.012290
Test Case '-[LoopTestTests testArray]' passed (0.165 seconds).
Test Case '-[LoopTestTests testDictionary]' started.
2012-08-02 17:14:22.273 otest[388:303] init:100000
2012-08-02 17:14:22.274 otest[388:303] MULogTimeintervalBegin:DictionaryTest
2012-08-02 17:14:22.284 otest[388:303]for statement allValues:0.010566
2012-08-02 17:14:22.307 otest[388:303]for statement allKeys:0.022377
2012-08-02 17:14:22.330 otest[388:303]enumeration:0.023914
Test Case '-[LoopTestTests testDictionary]' passed (0.217 seconds).
可以看出对于数组来说,for-in方式遍历速度是最快的,普通风格的for和block方式速度差不多。对于字典来说,allValues方式遍历最快,allKeys和block差不多。
那么,为什么会这样呢?
NSArray:
for (NSInteger i = 0; i < count; ++i) {
[sum appendString:[testArray objectAtIndex:i]];
}
这里由于存在:[objectAtIndex:i]这样的取操作,所以速度会有所下降。
而
for(NSString* item in testArray) {
[sum appendString:item];
}
尽管也有取操作,但是绕开了oc的message机制,速度会快一点。也有可能是编译器为了for-in作了优化。
block为什么会慢一些这个有待研究。
NSDictionary:
for (id akey in [testDic allKeys]) {
[sum appendString:[testDic objectForKey:akey]];
}
这个就很明显了,第二种方法多了一次objectForKey的操作。block的话有待研究。
=================================================================================
google了一下,stackoverflow上面有类似的讨论:点击打开链接
大意是:for-in语法会对容器里面的元素的内存地址建立一个缓冲,遍历的时候从缓冲直接取得元素的地址而不是通过调用方法来获取,所以效率比较高。另外,这也是不能在循环体中修改容器元素的原因之一。