Для чего используется аргумент BOOL *stop для enumerateObjectsUsingBlock:?

В последнее время я часто использую enumerateObjectsUsingBlock: для быстрого перечисления, и мне трудно понять использование BOOL *stop в блоке перечисления.

Ссылка на класс NSArray указывает

stop: ссылка на логическое значение. Блок может установить значение YES, чтобы остановить дальнейшую обработку массива. Аргумент stop является аргументом только для выхода. Вы должны только когда-либо устанавливать это логическое значение на YES внутри блока.

Тогда, конечно, я могу добавить в свой блок следующее, чтобы остановить перечисление:

if (idx == [myArray indexOfObject:[myArray lastObject]]) {
    *stop = YES;
}

Из того, что я смог сказать, явная установка *stop на YES не имеет никаких негативных побочных эффектов. Кажется, что перечисление автоматически останавливается в конце массива. Так действительно ли необходимо использовать *stop в блоке?


person Mick MacCallum    schedule 10.09.2012    source источник


Ответы (1)


Аргумент stop блока позволяет остановить перечисление преждевременно. Это эквивалент break из обычного цикла for. Вы можете игнорировать его, если хотите просмотреть каждый объект в массиве.

for( id obj in arr ){
    if( [obj isContagious] ){
        break;    // Stop enumerating
    }

    if( ![obj isKindOfClass:[Perefrigia class]] ){
        continue;    // Skip this object
    }

    [obj immanetizeTheEschaton];
}

[arr enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    if( [obj isContagious] ){
        *stop = YES;    // Stop enumerating
        return;
    }

    if( ![obj isKindOfClass:[Perefrigia class]] ){
        return;    // Skip this object
    }

    [obj immanentizeTheEschaton];
}];

Это выходной параметр, поскольку он является ссылкой на переменную из вызывающей области. Его нужно установить внутри вашего блока, но читать внутри enumerateObjectsUsingBlock:, точно так же, как NSError обычно передаются обратно в ваш код из вызовов фреймворка.

- (void)enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))block {
    // N.B: This is probably not how this method is actually implemented!
    // It is just to demonstrate how the out parameter operates!

    NSUInteger idx = 0;
    for( id obj in self ){

        BOOL stop = NO;

        block(obj, idx++, &stop);

        if( stop ){
            break;
        }
    }
}
person jscs    schedule 10.09.2012
comment
Обратите внимание, что флаг stop носит рекомендательный характер; например, перечисление может продолжаться в течение некоторого неопределенного числа итераций в параллельном случае. т.е. вы не должны безоговорочно устанавливать переменную __block при каждом проходе через перечисление и ожидать, что это будет последнее значение при использовании stop для досрочного завершения. Вы всегда должны сочетать нет, использовать этот объект с настройкой stop = YES;. - person bbum; 10.09.2012
comment
@bbum, можете ли вы уточнить, применяется ли постоянное поведение только к параллельному перечислению? Хотя в этом случае это вполне понятно, это не задокументировано, и было бы довольно неожиданным укусом в задницу для последовательного перечисления. - person jscs; 17.09.2013
comment
‹rdar://problem/15015199› теперь запрашивает разъяснения, поскольку в документации ничего не сказано, а должно быть. - person bbum; 18.09.2013