NSOperationQueue последовательная очередь FIFO - программирование
Подтвердить что ты не робот

NSOperationQueue последовательная очередь FIFO

Можно ли использовать объект NSoperationQueue в качестве последовательной очереди FIFO, установив его maxConcurrentOperationCount в 1?

Я отмечаю, что состояние docs...

Для очереди, для которой максимальное количество одновременных операций установлено в 1, это равносильно порядковой очереди. Однако вы никогда не должны полагаться на серийное выполнение объектов операций.

Означает ли это, что выполнение FIFO не гарантируется?

4b9b3361

Ответ 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 это равносильно порядковой очереди. Однако вы никогда не должны полагаться на серийное выполнение объектов операций. Изменения в готовности операции могут изменить результирующий порядок выполнения