NSOperationQueue: Trouble understanding the order

2019-05-18 00:11发布

问题:

This question already has an answer here:

  • NSOperationQueue serial FIFO queue 3 answers

I'm having trouble understanding the way NSOperationQueue works.

Say I have:

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        queue.maxConcurrentOperationCount=1;

        [queue addOperationWithBlock:^{
[someObject someSelector];
}];

        [queue addOperationWithBlock:^{
[someObject anotherSelector];
}];

The second block is being called even before the first block finishes - the opposite of what I want. I tried using – performSelectorOnMainThread:withObject:waitUntilDone: instead, but the second block is still being executed first - presumably because the block thread is not being completed on the main thread, and so it is not blocked with waitUntilDone. I added a break point inside my someSelector block, and it is reached after a break point inside the second block.

I don't quite get it. Help me!!

回答1:

If there are explicit dependencies between the operations, then use addDependency:

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount=1;

NSOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
    [someObject someSelector];
}];

NSOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
    [someObject anotherSelector];
}];

[operation2 addDependency:operation1];

[queue addOperation:operation1];
[queue addOperation:operation2];

If your operations are doing asynchronous activity, then you should define a custom operation, and only call completeOperation (which will post the isFinished message) when the asynchronous task is done).

//  SomeOperation.h

#import <Foundation/Foundation.h>

@interface SomeOperation : NSOperation

@end

and

//  SomeOperation.m

#import "SomeOperation.h"

@interface SomeOperation ()
@property (nonatomic, readwrite, getter = isFinished)  BOOL finished;
@property (nonatomic, readwrite, getter = isExecuting) BOOL executing;
@end

@implementation SomeOperation

@synthesize finished  = _finished;
@synthesize executing = _executing;

#pragma Configure basic operation

- (id)init
{
    self = [super init];
    if (self) {
        _finished  = NO;
        _executing = NO;
    }
    return self;
}

- (void)start
{
    if ([self isCancelled]) {
        self.finished = YES;
        return;
    }

    self.executing = YES;

    [self main];
}

- (void)completeOperation
{
    self.executing = NO;
    self.finished  = YES;
}

- (void)main
{
    // start some asynchronous operation

    // when it's done, call `completeOperation`
}

#pragma mark - Standard NSOperation methods

- (BOOL)isConcurrent
{
    return YES;
}

- (void)setExecuting:(BOOL)executing
{
    [self willChangeValueForKey:@"isExecuting"];
    _executing = executing;
    [self didChangeValueForKey:@"isExecuting"];
}

- (void)setFinished:(BOOL)finished
{
    [self willChangeValueForKey:@"isFinished"];
    _finished = finished;
    [self didChangeValueForKey:@"isFinished"];
}

@end

Thus, with the following code, it won't start operation2 until the asynchronous task initiated in main in SomeOperation object, operation1, calls its completeOperation method.

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount=1;

NSOperation *operation1 = [[SomeOperation alloc] init];

NSOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
    [someObject anotherSelector];
}];

[operation2 addDependency:operation1];

[queue addOperation:operation1];
[queue addOperation:operation2];