When you do the app, you always have to iterate through the array or dictionary multiple times.
What kind of traversal method is faster? I did the following tests:
First define the test macros:?
123456789 |
#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 |
Then write the test code:
Nsarray:?
123456789101112131415161718192021222324252627282930 |
- (
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"
);
}
|
Nsdictionary:?
123456789101112131415161718192021222324252627 |
- (
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"
);
}
|
Here are the test results:
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).
It can be seen that for arrays, the for-in way is the fastest, the normal style for and block mode speed is similar. For dictionaries, the Allvalues way is the fastest, AllKeys and block are similar.
So, why is this so?
Nsarray:?
123 |
for ( NSInteger i = 0; i < count; ++i) { [sum appendString:[testArray objectAtIndex:i]]; } |
This is due to the existence of: [OBJECTATINDEX:I] Such a fetch operation, so the speed will be reduced.
and?
123 |
for ( NSString * item in testArray) { [sum appendString:item]; } |
Although there is a take action, it is faster to bypass the message mechanism of OC. It is also possible that the compiler was optimized for for-in.
Why is block slower? This remains to be studied.
Nsdictionary:?
123 |
for ( id akey in [testDic allKeys]) { [sum appendString:[testDic objectForKey:akey]]; } |
This is obvious, the second method is more Objectforkey operation. Block's words need to be studied.
Google a bit, StackOverflow above has a similar discussion: Click to open the link
The main idea is that the for-in syntax will create a buffer for the memory address of the element inside the container, and it is more efficient to take the address of the element directly from the buffer, rather than by invoking the method. In addition, this is one of the reasons why container elements cannot be modified in the loop body.