150

明らかな違いに加えて:

  • enumerateObjectsUsingBlockインデックスとオブジェクトの両方が必要な場合に使用します
  • enumerateObjectsUsingBlockローカル変数を変更する必要がある場合は使用しないでください(これについては間違っていました。bbumの回答を参照してください)

一般的に、いつうまくいくかはenumerateObjectsUsingBlock良いか悪いかと考えられていますfor (id obj in myArray)か?長所/短所は何ですか(たとえば、パフォーマンスは多かれ少なかれ)?

4

6 に答える 6

354

Ultimately, use whichever pattern you want to use and comes more naturally in the context.

While for(... in ...) is quite convenient and syntactically brief, enumerateObjectsUsingBlock: has a number of features that may or may not prove interesting:

  • enumerateObjectsUsingBlock: will be as fast or faster than fast enumeration (for(... in ...) uses the NSFastEnumeration support to implement enumeration). Fast enumeration requires translation from an internal representation to the representation for fast enumeration. There is overhead therein. Block-based enumeration allows the collection class to enumerate contents as quickly as the fastest traversal of the native storage format. Likely irrelevant for arrays, but it can be a huge difference for dictionaries.

  • "Don't use enumerateObjectsUsingBlock when you need to modify local variables" - not true; you can declare your locals as __block and they'll be writable in the block.

  • enumerateObjectsWithOptions:usingBlock: supports either concurrent or reverse enumeration.

  • with dictionaries, block based enumeration is the only way to retrieve the key and value simultaneously.

Personally, I use enumerateObjectsUsingBlock: more often than for (... in ...), but - again - personal choice.

于 2010-12-20T05:02:30.100 に答える
83
于 2010-12-20T04:24:10.250 に答える
44

Although this question is old, things have not changed, the accepted answer is incorrect.

The enumerateObjectsUsingBlock API was not meant to supersede for-in, but for a totally different use case:

  • It allows the application of arbitrary, non-local logic. i.e. you don’t need to know what the block does to use it on an array.
  • Concurrent enumeration for large collections or heavy computation (using the withOptions: parameter)

Fast Enumeration with for-in is still the idiomatic method of enumerating a collection.

Fast Enumeration benefits from brevity of code, readability and additional optimizations which make it unnaturally fast. Faster than a old C for-loop!

A quick test concludes that in the year 2014 on iOS 7, enumerateObjectsUsingBlock is consistently 700% slower than for-in (based on 1mm iterations of a 100 item array).

Is performance a real practical concern here?

Definitely not, with rare exception.

The point is to demonstrate that there is little benefit to using enumerateObjectsUsingBlock: over for-in without a really good reason. It doesn't make the code more readable... or faster... or thread-safe. (another common misconception).

The choice comes down to personal preference. For me, the idiomatic and readable option wins. In this case, that is Fast Enumeration using for-in.

Benchmark:

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);

Results:

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

To answer the question about performance, I made some tests using my performance test project. I wanted to know which of the three options for sending a message to all objects in an array is the fastest.

The options were:

1) makeObjectsPerformSelector

[arr makeObjectsPerformSelector:@selector(_stubMethod)];

2) fast enumeration & regular message send

for (id item in arr)
{
    [item _stubMethod];
}

3) enumerateObjectsUsingBlock & regular message send

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

It turns out that makeObjectsPerformSelector was the slowest by far. It took twice as long as fast enumeration. And enumerateObjectsUsingBlock was the fastest, it was around 15-20% faster than fast iteration.

So if you're very concerned about the best possible performance, use enumerateObjectsUsingBlock. But keep in mind that in some cases the time it takes to enumerate a collection is dwarfed by the time it takes to run whatever code you want each object to execute.

于 2012-02-05T10:04:27.330 に答える
3

It's fairly useful to use enumerateObjectsUsingBlock as an outer loop when you want to break nested loops.

e.g.

[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;
      }
    }
  }
}];

The alternative is using goto statements.

于 2015-08-28T16:56:59.333 に答える
1

Thanks to @bbum and @Chuck for starting comprehensive comparisons on performance. Glad to know it's trivial. I seem to have gone with:

  • for (... in ...) - as my default goto. More intuitive to me, more programming history here than any real preference - cross language reuse, less typing for most data structures due to IDE auto complete :P.

  • enumerateObject... - when access to object and index is needed. And when accessing non-array or dictionary structures (personal preference)

  • for (int i=idx; i<count; i++) - for arrays, when I need to start on a non-zero index

于 2015-12-30T11:30:22.597 に答える