Цель c "для каждого" (быстрое перечисление) - оценка коллекции?

Кажется, из эксперимента, что выражение коллекции оценивается только один раз. Рассмотрим этот пример:

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

Вывод:

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

указывающий, что [self fcn] вызывается только один раз.

Может ли кто-нибудь подтвердить, что это указано (в отличие от просто наблюдаемого) поведения?

То, что я имею в виду, делает что-то вроде этого:

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

вместо этого:

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

Мысли?

Ответ 1

Этот тип цикла for называется "быстрым перечислением" (смотрите объект NSFastEnumeration). В документации Apple говорится, что в выражении "для obj in expression" выражение дает объект, соответствующий протоколу NSFastEnumeration, поэтому я предполагаю, что правильное поведение: функция вызывается один раз, итератор создается один раз и используется в цикле.

Ответ 2

На практике collection оценивается только один раз, например, вы можете проверить источник clang. Когда в документации недостаточно четко сказано, вам нужно доверять реализации.

Да, я думаю, Apple должна уточнить документацию. Пожалуйста, напишите ошибку, Марк!

Один педантичный способ получить это - прочитать границу официальной документации, которую можно найти здесь. Он заявил, что

перечислитель имеет мутацию, так что, если вы попытаетесь изменить коллекцию во время перечисления, возникает исключение. Поскольку мутация объекта во время итерации запрещена, вы можете выполнять несколько перечислений одновременно.

поэтому коллекция, по которой вы итерации, не может измениться. Он не говорит, оценивает ли он collection в

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

один или два раза, результирующий объект не может измениться.

Ответ 3

Здравый смысл диктует это так. Но поскольку это не указано непосредственно в документации быстрого перечисления, почему бы вам не взглянуть на сам протокол:

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

Этот протокол не предоставляет такие методы, как nextObject или objectAtIndex, или что-то в этом роде, но вместо этого запрашивает массив объектов C, над которым затем выполняется итерация.

Несмотря на наличие сильных признаков, ни один документ не гарантирует, что выражение оценивается только один раз.

Ответ 4

Это часть поведения быстрого перечисления. Для некоторых глубоких знаний о реализации быстрого перечисления проверьте это сообщение в блоге на NSBlog: Внедрение быстрого перечисления.