NSOperationQueue последовательная очередь FIFO
Можно ли использовать объект NSoperationQueue
в качестве последовательной очереди FIFO, установив его maxConcurrentOperationCount
в 1?
Я отмечаю, что состояние docs...
Для очереди, для которой максимальное количество одновременных операций установлено в 1, это равносильно порядковой очереди. Однако вы никогда не должны полагаться на серийное выполнение объектов операций.
Означает ли это, что выполнение FIFO не гарантируется?
Ответы
Ответ 1
В большинстве случаев это будет FIFO. Тем не менее, вы можете настроить зависимости между NSOperations таким образом, чтобы операция, представленная в начале, позволяла другим операциям передавать ее в очереди до тех пор, пока не будут выполнены ее зависимости.
Это управление зависимостями - вот почему документы указывают, что FIFO-ness нельзя гарантировать. Если вы не используете зависимости, вы должны быть в порядке, чтобы полагаться на него.
Обновление:
NSOperation также имеет свойство queuePriority
, которое также может привести к выполнению операций в не-FIFO-порядке. Операция с наивысшим приоритетом без ожидающих зависимостей всегда будет выполняться в первую очередь.
Подкласс NSOperation может также переопределить -isReady
, что может привести к его возврату в очередь.
Таким образом, выполнение в вашей очереди гарантировано будет серийным, так как в этой очереди будет выполняться не более одной операции. Но Apple не может гарантировать FIFO; это зависит от того, что вы делаете с операциями, которые вы вложили.
Ответ 2
Очередь не является FIFO, как указано в документации. Вы можете сделать это строго FIFO, если вы убедитесь, что любая новая операция зависит от последней операции, добавленной в очередь, и что она может выполнять только одну операцию за раз. Решение Omar верное, но в более общем плане вы можете сделать следующее:
NSOperationQueue* queue = [[ NSOperationQueue alloc ] init];
queue.maxConcurrentOperationCount = 1;
NSOperation* someOperation = [ NSBlockOperation blockOperationWithBlock:^(void) { NSLog(@"Done.");} ];
if ( queue.operations.count != 0 )
[ someOperation addDependency: queue.operations.lastObject ];
Это работает, потому что queue.operations - это массив: все, что вы добавляете, не переупорядочивается (например, это не NSSet). Вы также можете просто добавить категорию в свой NSOperationQueue:
@interface NSOperationQueue (FIFOQueue)
- (void) addOperationAfterLast:(NSOperation *)op;
@end
@implementation NSOperationQueue (FIFOQueue)
- (void) addOperationAfterLast:(NSOperation *)op
{
if ( self.maxConcurrentOperationCount != 1)
self.maxConcurrentOperationCount = 1;
NSOperation* lastOp = self.operations.lastObject;
if ( lastOp != nil )
[ op addDependency: lastOp ];
[ self addOperation:op];
}
@end
и используйте [queue addOperationAfterLast: myOperation]. queuePriority не имеет ничего общего с FIFO, это связано с планированием заданий.
Изменить: следуя приведенному ниже замечанию, приостановка очереди, если проверка количества также недостаточна. Я считаю, что эта форма прекрасна (при тестировании это не создает состояние гонки и не падает).
Некоторая информация: https://developer.apple.com/library/mac/documentation/Cocoa/Reference/NSOperationQueue_class/#//apple_ref/occ/instp/NSOperationQueue/suspended
Ответ 3
Сделать простой FIFO, используя nsInvocationopration
Вам нужно будет настроить одну операцию на другую
Использование addDependency: method
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSInvocationOperation *oper1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doSth:) object:@"1"];
NSInvocationOperation *oper2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doSth:) object:@"2"];
NSInvocationOperation *oper3 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doSth:) object:@"3"];
[oper2 addDependency:oper1];
[oper3 addDependency:oper2];
//oper3 depends on oper2 wich depends on oper1
//order of execution will ber oper1->oper2->oper3
//Changing the oreder will not change the result
[queue addOperation:oper2];
[queue addOperation:oper3];
[queue addOperation:oper1];
- (void) doSth:(NSString*)str
{
NSLog(str); //log will be 1 2 3
//When you remove the addDependency calls, the logging result that i got where
//different between consecutive runs i got the following
//NSLog(str); //log will be 2 1 3
//NSLog(str); //log will be 3 1 2
}
Примечание: если вы используете NSInvocationOperation
, то установка maxConcurrentOperationCount
на 1 скорее всего сделает трюк вам, так как isReady не будет доступен вам для редактирования
Но maxConcurrentOperationCount
= 1 не будет хорошим решением, если вы планируете создавать свои собственные подклассы NSOperation
Так как в производных NSOperation вы можете переопределить функцию isReady
и не возвращать нет (представьте себе некоторую операцию, которая должна была бы ждать некоторых данных с сервера для правильной работы), в этих случаях вы возвращаете isReady no
до тех пор, пока вы действительно готовы
В этих случаях вам нужно добавить dependencies
между operations
внутри очереди
Из apple docs это равносильно порядковой очереди. Однако вы никогда не должны полагаться на серийное выполнение объектов операций. Изменения в готовности операции могут изменить результирующий порядок выполнения