- 当您需要索引和对象时,请使用
enumerateObjectsUsingBlock
当您需要修改局部变量时,请勿使用(我对此错误,参见bbum的答案)enumerateObjectsUsingBlock
当for (id obj in myArray)
也可以正常工作时,enumerateObjectsUsingBlock
通常被认为更好还是更差?有什么优缺点(例如,它的性能更好还是更差)?
enumerateObjectsUsingBlock
enumerateObjectsUsingBlock
当for (id obj in myArray)
也可以正常工作时,enumerateObjectsUsingBlock
通常被认为更好还是更差?有什么优缺点(例如,它的性能更好还是更差)?
最终,在上下文中使用您更想要使用且更自然的模式。
虽然for(... in ...)
非常方便且语法简洁,但enumerateObjectsUsingBlock:
具有许多可能或可能不会证明有趣的功能:
enumerateObjectsUsingBlock:
将与快速枚举一样快或更快(for (...in ...)
使用NSFastEnumeration
支持实现枚举)。 快速枚举需要从内部表示转换为用于快速枚举的表示。 这其中存在开销。 基于块的枚举允许集合类像本地存储格式的最快遍历一样快速枚举内容。 对于数组来说可能不相关,但对于字典来说可能是很大的差别。
"当您需要修改局部变量时,请勿使用enumerateObjectsUsingBlock" - 不正确; 您可以将本地变量声明为__block
,并在块中可写。
enumerateObjectsWithOptions:usingBlock:
支持并发或逆向枚举。
对于字典,基于块的枚举是检索键和值同时的唯一方法。
个人而言,我更经常使用enumerateObjectsUsingBlock:
而不是for(... in ...)
,但 - 再次 - 是个人选择。
enumerateObjectsUsingBlock
对于数组和集合仍然比快速枚举慢得多。我想知道为什么?http://iosdevelopertips.com/objective-c/high-performance-collection-looping-objective-c.html - Bob SprynenumerateObjectsUsingBlock
将每次块调用都包装在自动释放池中(至少在OS X 10.10中是这样)。这解释了与不这样做的for in
相比的性能差异。 - Pol对于简单的枚举,仅使用快速枚举(即for...in...
循环)是更加惯用的选项。块方法可能会稍微快一点,但在大多数情况下这并不重要——很少有程序是CPU限制的,即使是那些受限于计算本身而非循环的情况也很少成为瓶颈。
一个简单的循环也更易读。以下是两个版本的样板代码:
for (id x in y){
}
[y enumerateObjectsUsingBlock:^(id x, NSUInteger index, BOOL *stop){}];
即使你添加一个变量来跟踪索引,简单的循环更易于阅读。
那么什么时候应该使用enumerateObjectsUsingBlock:
呢?当你需要存储一个块以便稍后执行或在多个地方执行时。当你实际上将块用作一等函数而不是过度复杂的循环体替代品时,它非常有用。
enumerateObjectsUsingBlock:
的速度要么与快速枚举相同,要么更快。for(... in ...)
使用快速枚举,这需要集合提供一些内部数据结构的中间表示。正如您所指出的那样,这可能是无关紧要的。 - bbumenumerateObjects...
比使用循环的快速枚举实际上可能更慢。我运行了这个测试数千次;块和循环的主体都是相同的一行代码:[(NSOperation *)obj cancel];
。平均值为:使用快速枚举的循环 - -[JHStatusBar dequeueStatusMessage:] [Line: 147] Fast enumeration time (for..in..loop): 0.000009
,而对于块--[JHStatusBar dequeueStatusMessage:] [Line: 147] Enumeration time using block: 0.000043
。奇怪的是时间差异如此之大和一致,但显然这是一个非常具体的测试案例。 - chownenumerateObjectsUsingBlock
API的作用并不是取代for-in
,而是为了完全不同的用例:
withOptions:
参数)for-in
进行快速枚举仍然是枚举集合的惯用法。快速枚举具有简洁的代码、可读性和额外的优化,使其异常快速。比一个老的C for-loop更快!
一个快速测试结论是,在iOS 7上的2014年,enumerateObjectsUsingBlock
始终比for-in慢700%,基于一个100项数组的1mm次迭代)。
性能在这里是一个真正实际的关注点吗?
绝对不是,除了很少的例外。
重点是要证明,没有很好的理由使用enumerateObjectsUsingBlock:
而不是for-in
。它不能使代码更易读……或更快……或线程安全(另一个常见误解)。
选择取决于个人偏好。对我来说,惯用和可读性选项胜出。在这种情况下,使用for-in
进行快速枚举。
NSMutableArray *arr = [NSMutableArray array];
for (int i = 0; i < 100; i++) {
arr[i] = [NSString stringWithFormat:@"%d", i];
}
int i;
__block NSUInteger length;
i = 1000 * 1000;
uint64_t a1 = mach_absolute_time();
while (--i > 0) {
for (NSString *s in arr) {
length = s.length;
}
}
NSLog(@"For-in %llu", mach_absolute_time()-a1);
i = 1000 * 1000;
uint64_t b1 = mach_absolute_time();
while (--i > 0) {
[arr enumerateObjectsUsingBlock:^(NSString *s, NSUInteger idx, BOOL *stop) {
length = s.length;
}];
}
NSLog(@"Enum %llu", mach_absolute_time()-b1);
结果:
2014-06-11 14:37:47.717 Test[57483:60b] For-in 1087754062
2014-06-11 14:37:55.492 Test[57483:60b] Enum 7775447746
enumerateObjectsUsingBlock
实际上要慢 5 倍。这显然是由于自动释放池包装了块的每次调用,而在 for in
的情况下不会发生这种情况。 - PolenumerateObjectsUsingBlock:
仍然比较慢,慢了4倍。 - Cœurenumerate
语法,因为它感觉像FP,而且不想听性能分析。 - Adam Kaplanenumerate:
时还有更多的堆栈推送和弹出。 - Adam Kaplan[arr makeObjectsPerformSelector:@selector(_stubMethod)];
2) 快速枚举和正常消息发送
for (id item in arr)
{
[item _stubMethod];
}
3) enumerateObjectsUsingBlock和常规消息发送
[arr enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
{
[obj _stubMethod];
}];
结果表明,makeObjectsPerformSelector是迄今为止最慢的。它比快速枚举慢了两倍。而enumerateObjectsUsingBlock是最快的,比快速迭代快15-20%左右。
因此,如果你非常关注最佳性能,请使用enumerateObjectsUsingBlock。但要记住,在某些情况下,枚举集合所需的时间被运行每个对象要执行的任何代码所耗费的时间所淹没。
如果你想要打破嵌套循环,使用enumerateObjectsUsingBlock作为外循环是非常有用的。
例如:
[array1 enumerateObjectsUsingBlock:^(id obj1, NSUInteger idx, BOOL * _Nonnull stop) {
for(id obj2 in array2) {
for(id obj3 in array3) {
if(condition) {
// break ALL the loops!
*stop = YES;
return;
}
}
}
}];
for (... in ...)
- 作为我的默认goto。对我来说更直观,具有更多的编程历史而不是真正的偏好 - 跨语言重用,由于IDE自动完成大多数数据结构而减少打字:P。
enumerateObject...
- 当需要访问对象和索引时。以及当访问非数组或字典结构时(个人偏好)
for (int i=idx; i<count; i++)
- 对于数组,当我需要从非零索引开始时