何时使用enumerateObjectsUsingBlock vs.

Pau*_*ler 150 multithreading objective-c ios4 objective-c-blocks

除了明显的差异:

  • 使用enumerateObjectsUsingBlock时,你既需要索引和对象
  • enumerateObjectsUsingBlock当你需要修改局部变量时不要使用(我错了,见bbum的答案)

enumerateObjectsUsingBlock普遍认为是好还是坏的时候for (id obj in myArray)也将工作?有哪些优点/缺点(例如性能或多或少)?

bbu*_*bum 347

最终,使用您想要使用的模式,并在上下文中更自然地使用.

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

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

  • "当你需要修改局部变量时,不要使用enumerateObjectsUsingBlock" - 不是真的; 你可以声明你的本地人__block,他们将在街区中可写.

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

  • 使用词典,基于块的枚举是同时检索键和值的唯一方法.

就个人而言,我enumerateObjectsUsingBlock:经常使用for (... in ...),但是 - 再次 - 个人选择.

  • 哇,信息量很大.我希望我能接受这两个答案,但我会和Chuck一起去,因为它与我产生了更多的共鸣.此外,我在搜索__block时找到了您的博客(http://www.friday.com/bbum/2009/08/29/blocks-tips-tricks/)并学到了更多.谢谢. (16认同)
  • 为了记录,基于块的枚举并不总是"快速或快速"http://www.mikeabdullah.net/slow-block-based-dictionary-enumeration.html (8认同)
  • @VanDuTran块仅在您告诉它们在单独的线程上执行时才在单独的线程上执行.除非您使用枚举的并发选项,否则它将在与调用相同的线程上执行 (2认同)
  • 尼克洛克伍德就此做了一篇非常好的文章,似乎`enumerateObjectsUsingBlock`仍然比数组和集的快速枚举慢得多.我想知道为什么?http://iosdevelopertips.com/objective-c/high-performance-collection-looping-objective-c.html (2认同)
  • 虽然它是一种实现细节,但在这个答案中应该提到两个方法之间的区别,即`enumerateObjectsUsingBlock`用自动释放池包装每个块的调用(至少从OS X 10.10开始).这解释了与"for in"相比的性能差异,而不是那样做. (2认同)

Chu*_*uck 81

对于简单的枚举,简单地使用快速枚举(即for…in…循环)是更惯用的选项.块方法可能稍微快一些,但在大多数情况下这并不重要 - 很少有程序受CPU限制,即使这样,循环本身而不是内部计算也很少成为瓶颈.

一个简单的循环也读得更清楚.这是两个版本的样板:

for (id x in y){
}

[y enumerateObjectsUsingBlock:^(id x, NSUInteger index, BOOL *stop){
}];
Run Code Online (Sandbox Code Playgroud)

即使您添加一个变量来跟踪索引,简单循环也更容易阅读.

所以什么时候应该使用enumerateObjectsUsingBlock:?当您存储块以便稍后执行或在多个位置执行时.当你实际上使用一个块作为一流函数而不是一个循环体的过度替代时,这是很好的.

  • @bbum我自己的测试显示`enumerateObjects ...`实际上可能比使用循环的快速枚举慢.我跑了几千次这个测试; 块和循环的主体是相同的单行代码:`[(NSOperation*)obj cancel];`.平均值:快速枚举循环 - ` - [JHStatusBar dequeueStatusMessage:] [Line:147]快速枚举时间(for..in..loop):0.000009`和块 - ` - [JHStatusBar dequeueStatusMessage:] [Line:147使用块的枚举时间:0.000043`.很奇怪,时差是如此之大和一致,但显然,这是一个非常具体的测试案例. (7认同)
  • 在所有情况下,`enumerateObjectsUsingBlock:`将与快速枚举速度相同或更快.`for(... in ...)`使用快速枚举,要求集合提供内部数据结构的一些临时表示.如你所知,可能无关紧要. (5认同)
  • +1`当您存储块以便稍后执行或在多个位置执行时.当你实际上使用一个块作为一流的函数而不是一个环状体的过度替代时,它是有益的. (4认同)

Ada*_*lan 42

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

enumerateObjectsUsingBlockAPI并不意味着取代for-in,但对于一个完全不同的使用情况:

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

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

快速枚举可以从简洁的代码,可读性和额外的优化中受益,这使得它非常快速.比旧的C for循环更快!

快速测试得出的结论是,2014年iOS 7上的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);
Run Code Online (Sandbox Code Playgroud)

结果:

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
Run Code Online (Sandbox Code Playgroud)

  • 我可以确认在MacBook Pro Retina 2014上运行相同的测试,`enumerateObjectsUsingBlock`实际上慢了5倍.这显然是由于自动释放池包装了每个块的调用,这对于`for in`情况不会发生. (4认同)
  • 我确认`enumerateObjectsUsingBlock:`在真正的iPhone 6 iOS9上仍然慢了4倍,使用Xcode 7.x来构建. (2认同)

Lea*_*s2D 23

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

选项是:

1)makeObjectsPerformSelector

[arr makeObjectsPerformSelector:@selector(_stubMethod)];
Run Code Online (Sandbox Code Playgroud)

2)快速枚举和定期发送消息

for (id item in arr)
{
    [item _stubMethod];
}
Run Code Online (Sandbox Code Playgroud)

3)enumerateObjectsUsingBlock®ular message send

[arr enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) 
 {
     [obj _stubMethod];
 }];
Run Code Online (Sandbox Code Playgroud)

事实证明,makeObjectsPerformSelector是迄今为止最慢的.它花费的时间是快速枚举的两倍.而enumerateObjectsUsingBlock是最快的,它比快速迭代快15-20%.

因此,如果您非常关注最佳性能,请使用enumerateObjectsUsingBlock.但请记住,在某些情况下,枚举集合所花费的时间与运行您希望每个对象执行的任何代码所花费的时间相比是相形见绌的.