2

I apologize if this question is exceedingly simple, but I've Googled like crazy and am unable to find a suitable explanation for what this is.

for (id line in self.lines){
    [linesCopy addObject:[line copyWithZone:zone]];
}

I'm just learning Objective-C, and this is a form of for loop that I've never seen before. I'm familiar with the simple

for (int x = 1, x < 10, x++)

style of for loop.

Rob
  • 1,045
  • 13
  • 28

4 Answers4

11

From Cocoa Core Competencies: Enumeration:

Fast Enumeration

Several Cocoa classes, including the collection classes, adopt the NSFastEnumeration protocol. You use it to retrieve elements held by an instance using a syntax similar to that of a standard C for loop, as illustrated in the following example:

NSArray *anArray = // get an array;
for (id element in anArray) {
    /* code that acts on the element */
}

As the name suggests, fast enumeration is more efficient than other forms of enumeration.

In case you didn't know, id is an Objective-C type that basically means “a pointer to any Objective-C object”. Note that the pointer-ness of id is built in to it; you usually do not want to say id *.

If you expect the elements of anArray to be of a specific class, say MyObject, you can use that instead:

for (MyObject *element in anArray) {
    /* code that acts on the element */
}

However, neither the compiler nor the runtime will check that the elements are indeed instances of MyObject. If an element of anArray is not an instance of MyObject, you'll probably end up trying to send it a message it doesn't understand, and get a selector-not-recognized exception.

Community
  • 1
  • 1
rob mayoff
  • 375,296
  • 67
  • 796
  • 848
1

It's called a forin loop, also called fast enumeration. Basically, the syntax is:

for (SomeObjectIAmExpecting *localVariableName in anArrayOfObjects)
{
    if (![localVariableName isKindOfClass:[SomeObjectIAmExpecting class]]) continue; //To avoid errors.
     //do something to them
}
Steph Sharp
  • 11,462
  • 5
  • 44
  • 81
Undo
  • 25,519
  • 37
  • 106
  • 129
  • How about: for (Class *object in collection) – Wain May 23 '13 at 22:49
  • Thanks! This is one of those things that had impossible-to-search-precisely keywords in it. Does the expected object have to be (or be castable to) the same type of object as those in the array? – Rob May 23 '13 at 22:50
  • @RobertHall No, but remember that it will be whatever it is. If you are expecting an `NSString` and you get an `NSNumber`, you obviously won't be able to get the `length` of it. Because of this, you sometimes have to check the class. See edit. – Undo May 23 '13 at 22:52
  • @Undo Why use `if (!(foo == bar))` instead of `if (foo != bar)`? Same result but unusual to what you are doing. – rmaddy May 23 '13 at 22:59
  • @rmaddy Because I did it an odd way - it's done the 'normal person' way now. – Undo May 23 '13 at 23:01
  • @Undo It would probably be better to check each element's class using `isKindOfClass:` to allow for subclasses. It would also probably be a better example if it used `continue` instead of `return` when the element's class doesn't match. – rob mayoff May 23 '13 at 23:07
  • @robmayoff Why `continue`? – Undo May 23 '13 at 23:13
  • I might ask, why `return`? Using `return` makes the loop process all elements of the array up to the first non-passing element, and ignore the remaining elements. In my experience, this is not a common pattern. The common patterns are (1) finding the first element that passes the test and processing it only, and (2) processing all elements that pass (or fail) the test, regardless of position in the array. The common patterns use `continue`. – rob mayoff May 23 '13 at 23:18
  • @robmayoff WOW! I didn't see that! Thanks! – Undo May 23 '13 at 23:19
  • You're welcome. Congratulations on your WWDC scholarship. Perhaps I will see you there! – rob mayoff May 23 '13 at 23:19
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/30522/discussion-between-undo-and-rob-mayoff) – Undo May 23 '13 at 23:19
  • @robmayoff http://chat.stackoverflow.com/rooms/30522/discussion-between-undo-and-rob-mayoff – Undo May 23 '13 at 23:20
  • @stephsharp Ohhh... I worked hard on making that code bold :) – Undo Jun 10 '13 at 03:05
  • @Undo Oh, sorry! It looked a bit strange with the first line in a separate block of code to the rest of the for loop. – Steph Sharp Jun 10 '13 at 03:07
  • @StephSharp Yep - your edit improved the post. [I approved it](http://stackoverflow.com/review/suggested-edits/2283647). Thanks! – Undo Jun 10 '13 at 03:08
  • @robmayoff You here at WWDC? – Undo Jun 10 '13 at 03:09
1

It's the shorthand equivalent of this common form:

for (int i = 0; i < [self.lines count]; i++) {
    id line = [self.lines objectAtIndex:i];
    // ...
}

It's such a common looping idiom (walking through some collection, array, set, etc. an item at a time), that it's been turned into a shorthand form like this, called "fast enumeration".

In fact, in its internal implementation, it's actually slightly more faster than doing it yourself, so it's preferable both for clarity and performance.

Ben Zotto
  • 70,108
  • 23
  • 141
  • 204
  • 2
    It uses neither the `count` nor the `objectAtIndex:` messages. – rob mayoff May 23 '13 at 22:50
  • 1
    @robmayoff Obviously not; I didn't claim it did. But this is the common form that it replaces, and the OP is new to ObjC syntax. – Ben Zotto May 23 '13 at 22:51
  • Maybe "equivalent" is a strong word here - for one, you can fast enumerate over a set, which does not even implement `objectAtIndex:`, so in my book they are not "equivalent". But agreed, we are getting into details. – Monolo May 23 '13 at 22:54
1

It's a statement that can be used with classes that are conform to NSFastEnumeration protocol. When you have this available, the Objective-C programming guide suggest you to use it. Take a look here. It's a way to iterate over a collection without the traditional for (int i = 0; i < length; ++i) syntax.

Mind that it doesn't usually support deleting and inserting elements while iterating through this way (also by using normal for loops you should take care about indices in any case).

Basically all standard collections supports this way of iteration.

Jack
  • 131,802
  • 30
  • 241
  • 343