遍历列表应该是平时开发中最常见的一种操作了。说起遍历效率,相比于其他方面的因素(可维护性,易懂等),也许遍历的效率在你的代码中并不是那么重要。
但我们还是要了解一下从性能方面去考虑遍历这个问题,以便在一写特殊的case下知道怎么着手去优化。
下面是几种常用的遍历方式:
1. for (NSInteger i = 0 ; i < len; i++)
3. - (void)enumerateObjectsUsingBlock:(void (NS_NOESCAPE ^)(ObjectType obj, NSUInteger idx, BOOL *stop))block
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
2. enumerate-block性能最差,是快速枚举的近20倍。
3. NSEnumerator和普通的for循环差别不大,是apply的1.4倍