150

除了明显的区别:

  • enumerateObjectsUsingBlock当您需要索引和对象时使用
  • enumerateObjectsUsingBlock当您需要修改局部变量时不要使用(我错了,请参阅 bbum 的回答)

enumerateObjectsUsingBlock一般认为更好或更差的时候for (id obj in myArray)也可以工作?有哪些优点/缺点(例如,它或多或少的性能)?

4

6 回答 6

354

最终,使用您想要使用的任何模式,并且在上下文中更自然。

虽然for(... in ...)非常方便且语法简洁,enumerateObjectsUsingBlock:但具有许多可能会或可能不会被证明有趣的功能:

  • enumerateObjectsUsingBlock:将与快速枚举一样快或更快(for(... in ...)使用NSFastEnumeration支持来实现枚举)。快速枚举需要从内部表示转换为快速枚举的表示。其中有开销。基于块的枚举允许集合类以最快的速度遍历本机存储格式来枚举内容。可能与数组无关,但对字典可能有很大的不同。

  • “当你需要修改局部变量时,不要使用 enumerateObjectsUsingBlock”——不正确;您可以将您的本地人声明为__block,它们将在块中可写。

  • enumerateObjectsWithOptions:usingBlock:支持并发或反向枚举。

  • 对于字典,基于块的枚举是同时检索键和值的唯一方法。

就个人而言,我使用enumerateObjectsUsingBlock:的频率高于for (... in ...),但 - 再次 - 个人选择。

于 2010-12-20T05:02:30.100 回答
83
于 2010-12-20T04:24:10.250 回答
44

虽然这个问题很老,但事情没有改变,接受的答案是不正确的。

enumerateObjectsUsingBlockAPI 并不是要取代for-in,而是用于完全不同的用例:

  • 它允许应用任意的非本地逻辑。即,您不需要知道块在数组上使用它的作用。
  • 大型集合或繁重计算的并发枚举(使用withOptions:参数)

快速枚举for-in仍然是枚举集合的惯用方法。

快速枚举受益于代码的简洁性、可读性和额外的优化,使其异常快速。比旧的 C for 循环更快!

一项快速测试得出结论,在 2014 年,iOS 7的运行enumerateObjectsUsingBlock速度始终比 for-in 慢 700%(基于 100 项数组的 1mm 迭代)。

性能在这里是一个真正的实际问题吗?

绝对不是,除了极少数例外。

关键是要证明在没有充分理由的情况下使用enumerateObjectsUsingBlock:over几乎没有什么好处。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
于 2014-06-11T19:18:04.210 回答
24

为了回答有关性能的问题,我使用我的性能测试项目进行了一些测试。我想知道向数组中的所有对象发送消息的三个选项中哪个是最快的。

选项是:

1) makeObjectsPerformSelector

[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。但请记住,在某些情况下,枚举集合所花费的时间与运行您希望每个对象执行的任何代码所花费的时间相比相形见绌。

于 2012-02-05T10:04:27.330 回答
3

当您想打破嵌套循环时,将 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;
      }
    }
  }
}];

另一种方法是使用 goto 语句。

于 2015-08-28T16:56:59.333 回答
1

感谢@bbum 和@Chuck 开始对性能进行全面比较。很高兴知道这是微不足道的。我似乎已经走了:

  • for (... in ...)- 作为我的默认转到。对我来说更直观,这里的编程历史比任何真正的偏好都要多 - 跨语言重用,由于 IDE 自动完成,大多数数据结构的输入更少:P。

  • enumerateObject...- 当需要访问对象和索引时。以及访问非数组或字典结构时(个人喜好)

  • for (int i=idx; i<count; i++)- 对于数组,当我需要从非零索引开始时

于 2015-12-30T11:30:22.597 回答