问题
Is it possible to use an NSoperationQueue
object as a serial FIFO queue by setting its maxConcurrentOperationCount
to 1?
I note that the docs state...
For a queue whose maximum number of concurrent operations is set to 1, this equates to a serial queue. However, you should never rely on the serial execution of operation objects.
Does this mean that FIFO execution is not guaranteed?
回答1:
In most cases, it will be FIFO. However, you can set up dependencies between NSOperations such that an operation submitted early will let other operations pass it by in the queue until its dependencies are satisfied.
This dependency management is why the docs indicate that FIFO-ness cannot be guaranteed. If you're not using dependencies, though, you should be fine to rely on it.
Update:
NSOperation also has a queuePriority
property, which can also cause operations to execute in non-FIFO order. The highest-priority operation with no pending dependencies will always execute first.
An NSOperation subclass might also override -isReady
, which could cause it to move back in the queue.
So execution on your queue is guaranteed to be serial, in that no more than one operation will run at a time in this queue. But Apple can't guarantee FIFO; that depends on what you're doing with the operations you put in.
回答2:
The queue is not FIFO as mentioned by the documentation. You can make it strictly FIFO if you ensure that any new operation depends on the last operation added in the queue and that it can only run a single operation at a time. Omar solution is correct, but more generally, you can do the following:
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 ];
This works because queue.operations is an array: whatever you add is not reordered (it is not a NSSet for instance). You can also simply add a category to your 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
and use [queue addOperationAfterLast:myOperation]. queuePriority has nothing to do with FIFO, it is related to job scheduling.
Edit: following a comment below, suspending the queue if checking for the count is also not sufficient. I believe this form is fine (upon testing, this does not create a race condition and does not crash).
Some info: https://developer.apple.com/library/mac/documentation/Cocoa/Reference/NSOperationQueue_class/#//apple_ref/occ/instp/NSOperationQueue/suspended
回答3:
To make a simple FIFO using nsInvocationopration You will need to set one operation to be depended on the other Using 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
}
Note: if you are using NSInvocationOperation
then setting the maxConcurrentOperationCount
to 1 would most likely do the trick to you, since isReady will not be editable by you
But maxConcurrentOperationCount
= 1 would not be a good solution if you are planing to create your own subclasses of NSOperation
Since in NSOperation derivatives you could override the isReady
function and return no, (imagine some operation that would need to wait some data from a server to in order to function properly) in these cases you would return isReady no
until you are really ready
In these cases you will need to add dependencies
between operations
inside the queue
From apple docs this equates to a serial queue. However, you should never rely on the serial execution of operation objects. Changes in the readiness of an operation can change the resulting execution order
来源:https://stackoverflow.com/questions/10948804/nsoperationqueue-serial-fifo-queue