7

Есть ли способ имитировать оператор break в блоке dispatch_apply()?Как «сломать» из dispatch_apply()?

Е.Г., каждый Cocoa API Я видел дело с перечислением блоков имеет «стоп» параметр:

[array enumerateObjectsUsingBlock:^(id obj, NSUInteger i, BOOL *stop) { 
    if ([obj isNotVeryNice]) { 
     *stop = YES; // No more enumerating! 
    } else { 
     NSLog(@"%@ at %zu", obj, i); 
    } 
}]; 

Есть ли что-то подобное для НОД?

ответ

4

Я не думаю, что dispatch_apply поддерживает это. Лучший способ, которым я могу имитировать, - сделать логическую переменную __block и проверить ее в начале блока. Если он установлен, быстро выручите. Вам все равно придется запускать блок через остальные итерации, но это будет быстрее.

1

Вы не можете break a dispatch_apply так как это нелогично.

В -enumerateObjectsUsingBlock: перерыв четко определен, поскольку функции выполняются последовательно. Но в dispatch_apply функции выполняются параллельно. Это означает, что при вызове «block» на может быть запущен i=4-й вызов. Если вы break на i=3, должен ли вызов i=4 по-прежнему работать?

@BJ ответ ближайший вы можете сделать, но всегда будет «перелив».

+0

Ну, это не совсем так; в то время как это верно, что enumerateObjectsUsingBlock: является последовательным, есть также enumerateObjectsWithOptions: usingBlock :. Этот параметр «options» может использоваться для указания того, что перечисление должно происходить одновременно. Я не уверен, как они это делают внутренне, но я бы предположил, что это сделано с помощью диспетчера_группы, которая позволит более прямое управление. –

+0

Но дело в том, что enumerateObjectsWithOptions: usingBlock: все еще имеет параметр * stop. –

+1

Было бы логично поддерживать остановку в dispatch_apply(), но это не имело смысла в рамках целей дизайна. Утверждение о том, что флаг остановки в 'enumerateObjectsUsingBlock:' существует из-за предполагаемого последовательного выполнения, неверен; они полностью ортогональны. – bbum

14

По дизайну, dispatch_*() API не имеют понятия отмены. Причина этого заключается в том, что практически всегда верно, что ваш код поддерживает концепцию, когда остановиться или нет, и, таким образом, также поддерживая, что в API-интерфейсе dispatch _ *() будет избыточным (и при избыточности появятся ошибки).

Таким образом, если вы хотите «остановить раннее» или отменить ожидающие элементы в очереди отправки (независимо от того, как они были установлены в очередь), вы можете сделать это, разделив бит состояния с заблокированными блоками, что позволит вам Отмена.

if (is_canceled()) return; 

Или:

__block BOOL keepGoing = YES; 
dispatch_*(someQueue, ^{ 
    if (!keepGoing) return; 
    if (weAreDoneNow) keepGoing = NO; 
} 

Обратите внимание, что оба enumerateObjectsUsingBlock: и enumerateObjectsWithOptions:usingBlock: оба поддерживают отмену, потому что API в другой роли. Вызов метода перечисления синхронный, даже если фактическое выполнение перечислимых блоков может быть полностью параллельным в зависимости от параметров.

Таким образом, установка *stopFlag=YES сообщает, что перечисление останавливается. Однако он не гарантирует, что он немедленно остановится в параллельном случае. Перечисление может, по сути, выполнить еще несколько уже заблокированных блоков перед остановкой.

(Можно было бы подумать, что было бы разумнее вернуть BOOL, чтобы указать, должно ли перечисление продолжаться. Для этого потребовалось бы, чтобы перечислимый блок выполнялся синхронно даже в параллельном случае, так что возвращаемое значение может быть проверено. Это было бы значительно менее эффективно.)