Ответ 1
Я не эксперт в NSOperation или NSOperationQueues, но я думаю, что код ниже немного лучше, хотя я думаю, что у него есть некоторые предостережения. Вероятно, достаточно для некоторых целей, но не является общим решением для concurrency:
- (NSOperation *)executeBlock:(void (^)(void))block
inQueue:(NSOperationQueue *)queue
completion:(void (^)(BOOL finished))completion
{
NSOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:block];
NSOperation *completionOperation = [NSBlockOperation blockOperationWithBlock:^{
completion(blockOperation.isFinished);
}];
[completionOperation addDependency:blockOperation];
[[NSOperationQueue currentQueue] addOperation:completionOperation];
[queue addOperation:blockOperation];
return blockOperation;
}
Теперь можно использовать его:
- (void)tryIt
{
// Create and configure the queue to enqueue your operations
backgroundOperationQueue = [[NSOperationQueue alloc] init];
// Prepare needed data to use in the operation
NSMutableString *string = [NSMutableString stringWithString:@"tea"];
NSString *otherString = @"for";
// Create and enqueue an operation using the previous method
NSOperation *operation = [self executeBlock:^{
NSString *yetAnother = @"two";
[string appendFormat:@" %@ %@", otherString, yetAnother];
}
inQueue:backgroundOperationQueue
completion:^(BOOL finished) {
// this logs "tea for two"
NSLog(@"%@", string);
}];
// Keep the operation for later uses
// Later uses include cancellation ...
[operation cancel];
}
Некоторые ответы на ваши вопросы:
-
Отмена. Обычно вы подклассифицируете NSOperation, чтобы вы могли проверить
self.isCancelled
и вернуться раньше. См. этот поток, это хороший пример. В текущем примере вы не можете проверить, отменена ли операция из блока, который вы поставляете, чтобы сделатьNSBlockOperation
, потому что в то время еще нет такой операции. ОтменаNSBlockOperation
при вызове блока, по-видимому, возможна, но громоздкая.NSBlockOperation
предназначены для особых случаев. Если вам требуется отмена, вы лучше подклассифицируетеNSOperation
:) -
Я не вижу здесь проблемы. Хотя обратите внимание на две вещи. a) Я изменил метод do, чтобы запустить блок завершения в текущей очереди. b) в качестве параметра требуется очередь. Как сказал @Mike Weller, вам лучше поставить
background queue
, поэтому вам не нужно создавать по одному для каждой операции и выбирать, какую очередь использовать для запуска ваших вещей:) -
Думаю, да, вы должны сделать
string
atomic
. Не стоит забывать, что если вы поставите несколько операций в очередь, они могут не работать в этом порядке (обязательно), чтобы в вашемstring
вы могли получить очень странное сообщение. Если вам нужно выполнить одну операцию одновременно, вы можете сделать:[backgroundOperation setMaxConcurrentOperationCount:1];
, прежде чем запускать операции. В docs есть примечание, достойное чтения, хотя:Дополнительные операции Очередь операцийОперационная очередь выполняет свои очереди в операционных объектах на основе их приоритета и готовности. Если все объекты очереди в очереди имеют одинаковый приоритет и готовы к выполнению, когда они помещаются в очередь, то есть их метод isReady возвращает YES - они выполняются в том порядке, в котором они были отправлены в очередь. Для очереди, для которой максимальное количество одновременных операций установлено в 1, это равносильно порядковой очереди. Однако вы никогда не должны полагаться на серийное выполнение объектов операций. Изменения в готовности операции могут изменить результирующий порядок выполнения.
-
Я думаю, что после прочтения этих строк вы знаете:)