开发者

Cancelling NSOperationQueue from within NSOperation

开发者 https://www.devze.com 2023-01-05 10:53 出处:网络
I have some iPhone SDK 4.0 code which initializes an NSOperationQueue and then adds three classes (ClassA, ClassB, and ClassC) to run one after the other. ClassA, ClassB, and ClassC are all sub-classe

I have some iPhone SDK 4.0 code which initializes an NSOperationQueue and then adds three classes (ClassA, ClassB, and ClassC) to run one after the other. ClassA, ClassB, and ClassC are all sub-classes of NSOperation.

The relevant code is included below.

ClassA *classA = [[ClassA alloc] init];
ClassB *classB = [[ClassB alloc] init];
ClassC *classC = [[ClassC alloc] init];

[classB addDependency:classA];
[classC addDependency:classB];

NSOperationQueue *queue = [[NSOperationQueue alloc] init];

[queue addOperation:classA];
[queue addOperation:classB];
[queue addOperation:classC];

[classA release];
[classB release];
[classC release];
[queue release];

The reason for the dependencies is because classB should only run if classA completes its operation successfully. Likewise, classC should only run if classB completes successfully.

At the moment I am having difficulty figuring out how to prevent, for example, classB from running if classA does not complete successfully. Continuing with this example, I was thinking of somehow evoking [NSOperationQueue cancelAllOperations] from within classA but I don't know how to get a handle on the parent NSOperationQueue from within classA (which is an NSOperation sub-class). This was just my initial thought, so I would be open to any other better suggestions for achieving the same outcome!

There is conditional code within each of the classes to determine whether they have completed properly - at 开发者_运维问答the moment they are just NSLogging "Success" or "Fail" to the Console for debugging purposes. In a perfect world I would just like to be able to replace the NSLog(@"Fail") statement in each class with some code which will stop all of the other classes in the NSOperationQueue from running.

Any suggestions would be most welcome (and appreciated).


You could set a property in classA :

@property (readonly) BOOL completedSucessfully;

and set this to YES at the end of classA's main method.

Then, just check it at the start of classB.

- (void)main {
    if (NO == [[dependencies objectAtIndex:0] completedSucessfully])
        return;

Now, classB will just stop if classA reports failure.

NB You will probably need more error checking that in the example above i.e. making sure that you have dependencies, checking that it's the correct class etc.

- (void)main {
    for (id *temp in [self dependencies])
        if ([temp isKindOfClass:[ClassA class]])
            if (NO == [(ClassA *)temp finishedSucessfully])
                return;


I would suggest, if speed is not an issue, you can work synchronously. Else you can use:

[selector:@selctor(StartB) waitUntilTaskComplete:YES];


After viewing the WWDC 2015 session on advanced NSOperation techniques (highly recommended) I started using them in-depth in my own code. Here are some suggestions to achieve this

  1. From within an NSOperation you can call [self currentQueue] to get "The operation queue that started the operation or nil if the queue could not be determined." You could then call cancelAllOperations on the returned queue. Empirically I have had difficulty using this approach because if you explicitly run code on the main queue, have code in a closures/block, or call a third party library, then the queue returned may not be the initial queue at all. In that situation calling cancelAllOperations will not result in the expected behavior - instead you are canceling the operations on a different queue.

  2. Subclass NSOperation to include a property for the initial NSOperationQueue and subclass NSOperationQueue to set the property when the operation is added to the queue. Then call cancelAllOperations on self.initialQueue. This is the approach I'm using and works across all the scenarios mentioned above.

  3. Instead of canceling all operations at the queue level, you can call the operation "cancel" method and finish your operation. If your operations have been written to conform to Apple's operation guidelines, they all check isCancelled when starting, and abort processing if true. It's a subtle difference: when you cancel the queue operations, any operations that haven't been started will not be started at all. When you set the operations to isCancelled, subsequent operations are started but (should) finish shortly there after. This allows scenarios where later operations might perform some cleanup, error handling, or user notification.

0

精彩评论

暂无评论...
验证码 换一张
取 消