何时使用enumerateObjectsUsingBlock与for
除了明显的区别之外:
- 当你需要索引和对象时,使用
enumerateObjectsUsingBlock
-
当你需要修改局部variables时,不要使用(我错了,请看bbum的答案)enumerateObjectsUsingBlock
是否enumerateObjectsUsingBlock
一般认为更好或更糟的时候for (id obj in myArray)
也将工作? 有什么优点/缺点(例如,它或多或less的性能)?
最终,使用任何你想使用的模式,在上下文中更自然。
虽然for(... in ...)
非常方便且语法简洁,但enumerateObjectsUsingBlock:
具有许多可能会或可能不会令人感兴趣的function:
-
enumerateObjectsUsingBlock:
与快速枚举一样快或者更快(for(... in ...)
使用NSFastEnumeration
支持来实现枚举)。 快速枚举需要从内部表示到快速枚举的表示。 那里有头顶。 基于块的枚举允许集合类以尽可能快的方式枚举内容,快速遍历本机存储格式。 可能对数组无关紧要,但是对于字典可能会有很大的差别。 -
“当你需要修改局部variables时不要使用enumerateObjectsUsingBlock” – 不正确; 你可以将你的本地声明为
__block
并且它们在块中是可写的。 -
enumerateObjectsWithOptions:usingBlock:
支持并发或反向枚举。 -
用字典,基于块的枚举是同时检索关键字和值的唯一方法。
就我个人而言,我使用enumerateObjectsUsingBlock:
比for (... in ...)
更频繁,但是 – 再次 – 个人select。
对于简单的枚举,简单地使用快速枚举(即for…in…
循环)是更习惯的select。 块的方法可能稍微快一点,但是在大多数情况下这并不重要 – 很less有程序是CPU限制的,即使如此,循环本身而不是内部计算也是一个瓶颈。
一个简单的循环也读得更清楚。 这是两个版本的样板:
for (id x in y){ } [y enumerateObjectsUsingBlock:^(id x, NSUInteger index, BOOL *stop){ }];
即使添加一个variables来跟踪索引,简单的循环也更容易阅读。
所以当你应该使用enumerateObjectsUsingBlock:
当你存储一个块,以便稍后或在多个地方执行。 当你将一个块用作一个一stream的函数,而不是一个循环体的过度replace时,这是很好的。
虽然这个问题很老,事情没有改变, 接受的答案是不正确的。
enumerateObjectsUsingBlock
API不是为了替代for-in
,而是为了一个完全不同的用例:
- 它允许应用任意的非本地逻辑。 即,你不需要知道块在数组上使用它是什么。
- 并发枚举大型集合或大量计算(使用
withOptions:
参数)
使用for-in
快速枚举仍然是枚举集合的惯用方法。
快速枚举有利于代码简洁,可读性和额外的优化 ,使其不自然的快速。 比旧的C for循环更快!
一个快速testing得出结论,在2014年的iOS 7上, enumerateObjectsUsingBlock
总是比for-in慢100%(基于100mmarrays的1mm迭代)。
在这里performance是一个真正的实际问题?
绝对不是,除了罕见的例外。
重点是certificate使用enumerateObjectsUsingBlock:
几乎没有什么好处enumerateObjectsUsingBlock:
没有一个很好的理由。 它不会使代码更具可读性…或更快速…或线程安全。 (另一个常见的误解)。
select归结为个人偏好。 对我来说,地道和可读的选项是胜利的。 在这种情况下,这是使用for-in
快速枚举。
基准testing:
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
为了回答关于性能的问题,我使用我的性能testing项目做了一些testing。 我想知道发送消息给数组中所有对象的三个选项中哪一个是最快的。
选项是:
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]; }];
事实certificate,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; } } } }];
另一种方法是使用goto语句。
感谢@ bbum和@Chuck开始综合性能比较。 很高兴知道这是微不足道的。 我似乎已经走了:
-
for (... in ...)
– 作为我的默认转到。 对我来说更直观的是,这里有更多的编程历史比任何真正的偏好 – 跨语言重用,由于IDE自动完成,大多数数据结构的键入较less:P。 -
enumerateObject...
– 当需要访问对象和索引时。 而当访问非数组或字典结构(个人喜好) -
for (int i=idx; i<count; i++)
– 对于数组,当我需要从一个非零索引开始