Do NSOperations and their completionBlocks run con

2019-01-20 00:16发布

I've got a bunch of NSOperations added to a NSOperationQueue. The operation queue has the maxConcurrentOperationCount set to 1, so that the NSOperations run one after the other.

Now, in the completionBlock of a NSOperation I want to cancel all pending NSOperations by calling cancelAllOperations on the NSOperationQueue.

Is it safe to do this? Can I be sure that the start-method of the next operation is called only after the completionBlock of the previous operation has been fully executed? Or do the completionBlock of the previous operation and the task of the current operation run concurrently?

The reason why I'm asking: I use AFNetworking to execute a batch of AFHTTPRequestOperations and want to perform one request only if all previous requests of the batch were successful.

3条回答
叛逆
2楼-- · 2019-01-20 00:27

NSOperation establishes dependency only based on the completion states of operations, and not on the results of completed operations.

However, most of the scenarios that I encounter are such that, the execution of operations depend not only on the completion of some other operations, but also based on the results obtained from the completed operations.

I ended up doing like the below method, but still exploring if there is a better way:

1) Operation-A runs

2) Operation-A compeletes and its completionBlock runs

3) In the OperationA's completion block, check for the result obtained from Operation-A.

  • If result is X, create Operation-B and add to the queue.
  • If result is Y, create Operation-C and add to the queue.
  • If result is error, create Operation-D (usually an alert operation) and add to the queue

So, this ends up as a sequence of operations, that are dynamically added to the queue, depending on the result of completed operations.

查看更多
男人必须洒脱
3楼-- · 2019-01-20 00:28

My findings below no longer seem to be true. I've re-run the tests on iOS 8 and iOS 9 and the completion block of an operation always runs concurrently with the next operation. Currently, I don't see a way to make an operation wait for the previous completion block to finish.


I just tried this scenario in a sample project. Here is the result:

If the NSOperationQueue's maxConcurrentOperationCount is set to 1, an NSOperation's completionBlock and the next NSOperation in the queue run simultaneously.

But, if every NSOperation is linked to its previous operation by calling addDependency:, the execution of an operation waits until the previous operation's completionBlock has finished.

So, if you want to cancel the next operation in the completionBlock of the current operation and be sure that it is cancelled before it is started, you have to set dependencies between the NSOperations by calling addDependency:

查看更多
等我变得足够好
4楼-- · 2019-01-20 00:45

I came up with another seemingly better way to ensure that an operaion is executed only if certain conditions (based on the results of previously finished operations) are met, else, the operation is cancelled.

One important consideration here is that the condition check for running an operation should not be coded inside the operation subclass, thus allowing the operation subclass to be poratble across different scenarios and apps.

Solution: - Have a condition block property inside the subclass, and set whatever condition form where the operation is instantiated. - Override "isReady" getter of the NSOperation subclass, check the condition there, and thus determine if its ready for execution. - If [super isReady] is YES, which means the dependent operations are all finished, then evaluate the necessary condition. - If the condition check is passed, return YES. Else, set isCancelled to YES and return YES for isReady

Code: In the interface file have the block property:

typedef BOOL(^ConditionBlock)(void);

@property (copy) ConditionBlock conditionBlock;

In the implementation, override isReady, and cancelled:

@implementation ConditionalOperation

- (BOOL)isReady {
        if([super isReady]) {
            if(self.conditionBlock) {
                if(!self.conditionBlock()) {
                    [self setCancelled:YES];
                }
                return YES;
            } else {
                return YES;
            }
        } else {
            return NO;
        }
    }
查看更多
登录 后发表回答