除了明显的区别:
- 使用
enumerateObjectsUsingBlock
时,你既需要索引和对象 (我错了,请参见bbum的答案)enumerateObjectsUsingBlock
当您需要修改局部变量时不要使用
被enumerateObjectsUsingBlock
普遍认为是好还是坏的时候for (id obj in myArray)
也会工作?优点/缺点是什么(例如,或多或少的性能)?
除了明显的区别:
enumerateObjectsUsingBlock
时,你既需要索引和对象enumerateObjectsUsingBlock
当您需要修改局部变量时不要使用被enumerateObjectsUsingBlock
普遍认为是好还是坏的时候for (id obj in myArray)
也会工作?优点/缺点是什么(例如,或多或少的性能)?
Answers:
最终,使用您想要使用的任何模式,并且在上下文中更加自然。
虽然for(... in ...)
很方便且语法简短,enumerateObjectsUsingBlock:
但其中的一些功能可能会或可能不会很有趣:
enumerateObjectsUsingBlock:
将比快速枚举快或快(for(... in ...)
使用NSFastEnumeration
支持来实现枚举)。快速枚举需要从内部表示形式转换为快速枚举的表示形式。其中有开销。基于块的枚举允许集合类枚举内容的速度最快地遍历本机存储格式。与数组可能无关紧要,但对字典而言可能是巨大的不同。
“当您需要修改局部变量时,请不要使用enumerateObjectsUsingBlock”-不正确;您可以将您的本地人声明为__block
,他们将在代码块中可写。
enumerateObjectsWithOptions:usingBlock:
支持并发或反向枚举。
对于字典,基于块的枚举是同时检索键和值的唯一方法。
就个人而言,我使用的enumerateObjectsUsingBlock:
次数多于for (... in ...)
,但-还是个人选择。
enumerateObjectsUsingBlock
它仍然比数组和集合的快速枚举慢得多。我想知道为什么?iosdevelopertips.com/objective-c/...
enumerateObjectsUsingBlock
使用自动释放池(至少从OS X 10.10起)包装每次调用该块的两种方法之间的差异。这解释了性能差异与for in
不这样做的差异。
对于简单的枚举,更简单的选择是使用快速枚举(即for…in…
循环)。块方法可能稍快一些,但是在大多数情况下并没有太大关系-很少有程序受CPU限制,即使那样,循环本身而不是内部计算很少会成为瓶颈。
一个简单的循环也可以更清晰地读取。这是两个版本的样板:
for (id x in y){
}
[y enumerateObjectsUsingBlock:^(id x, NSUInteger index, BOOL *stop){
}];
即使添加变量来跟踪索引,简单的循环也更易于阅读。
那么什么时候应该使用enumerateObjectsUsingBlock:
?当您存储一个块以便以后或在多个地方执行时。当您实际上将块用作一流函数而不是过度使用替代循环体时,这非常有用。
enumerateObjectsUsingBlock:
在所有情况下,其速度都将与快速枚举相同或更快。 for(... in ...)
使用快速枚举,该枚举要求集合提供内部数据结构的一些临时表示。如您所述,可能无关紧要。
When you're storing a block to execute later or in multiple places. It's good for when you're actually using a block as a first-class function rather than an overwrought replacement for a loop body.
enumerateObjects...
使用循环实际上可能比慢速枚举慢。我进行了数千次测试。块和循环的主体是同一行代码:[(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
。奇怪的是,时差是如此之大且一致,但是显然,这是一个非常具体的测试用例。
尽管这个问题很旧,但情况没有改变,但接受的答案是错误的。
该enumerateObjectsUsingBlock
API并非要取代for-in
,而是针对完全不同的用例:
withOptions:
参数)for-in
仍然是枚举集合的惯用方法。快速枚举得益于代码简洁,可读性和其他优化功能,这些特性使其异常快速。比旧的C for循环快!
一项快速测试得出的结论是,在2014年的iOS 7上,enumerateObjectsUsingBlock
其性能始终比入门级慢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
enumerateObjectsUsingBlock
实际上要慢5倍。显然,这是由于自动释放池包装了该块的每次调用,在这种for in
情况下不会发生。
enumerateObjectsUsingBlock:
使用Xcode 7.x构建的真实iPhone 6 iOS9仍然慢4倍。
enumerate
语法,因为它感觉像FP,并且不想听性能分析。
enumerate:
为了回答有关性能的问题,我使用性能测试项目进行了一些测试。我想知道将消息发送到数组中所有对象的三个选项中哪一个最快。
选项包括:
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。但是请记住,在某些情况下,枚举集合所花费的时间与运行每个对象要执行的任何代码所花费的时间相形见war。
当您想中断嵌套循环时,将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语句。