views:

70

answers:

4

It seems from experimentation that the collection expression is evaluated only once. Consider this example:

static NSArray *a;

- (NSArray *)fcn
{
    if (a == nil)
        a = [NSArray arrayWithObjects:@"one", @"two", @"three", nil];
    NSLog(@"called");
    return a;
}

...

for (NSString *s in [self fcn])
    NSLog(@"%@", s);

The output is:

2010-10-07 07:37:31.419 WidePhotoViewer Lite[23694:207] called
2010-10-07 07:37:31.420 WidePhotoViewer Lite[23694:207] one
2010-10-07 07:37:31.425 WidePhotoViewer Lite[23694:207] two
2010-10-07 07:37:31.425 WidePhotoViewer Lite[23694:207] three

indicating that [self fcn] is called only once.

Can anyone confirm that this is the specified (as opposed to merely observed) behavior?

What I have in mind is doing something like this:

for (UIView *v in [innerView subviews]) {

instead of this:

NSArray *vs = [innerView subviews];
for (UIView *v in vs) {

Thoughts?

+4  A: 

This kind of for loop is called a "fast enumeration" (look at the NSFastEnumeration object). Apple's documentation says that in "for obj in expression", expression yields an object that conforms to the NSFastEnumeration protocol, so I guess that's the correct behaviour: the function is called once, an iterator is created once and used in the loop.

Shmurk
A: 

This is part of the behavior of fast enumeration. For some in-depth knowledge of the implementation of fast enumeration check out this blog post on NSBlog: Implementing Fast Enumeration.

Benjamin Egelund-Müller
+1  A: 

Common sense dictates it to be that way. But since it is not directly stated in the Fast Enumeration Documentation, why don't you have a look at the protocol itself:

http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/NSFastEnumeration_protocol/Reference/NSFastEnumeration.html

This protocol is not providing methods like nextObject, or objectAtIndex, or something like that, but instead requests a C array of objects, over which is then iterated.

Although there are strong indications, no document guarantees you that the expression is only evaluated once.

w.m
+2  A: 

In practice, collection is only evaluated once, you can check the source of clang, for example. When the documentation doesn't say things precise enough, you need to trust the implementation.

Yes I think Apple should clarify documentation. Please file a bug, Marc!

One pedantic way to get this is to read the margin of the official documentation, which can be found here. It's stated that

the enumerator has a mutation guard so that if you attempt to modify the collection during enumeration, an exception is raised. Since mutation of the object during iteration is forbidden, you can perform multiple enumerations concurrently.

so, the collection over which you iterate can't change anyway. It doesn't say whether it evaluates collection in

for(id obj in collection) { ... } 

once or twice, the resulting object can't change.

Yuji
+1 just for suggesting a bug-filing!
Wevah