views:

57

answers:

1

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).

+2  A: 

Hi,

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;
deanWombourne
Thanks Dean. Exactly what I needed :)
Skoota
This design has problems. The [dependencies objectAtIndex:0] could be nil.
falconcreek
read the second code snippet ;) the first was just included to be quickly readable, the second is the one I'd actually use.
deanWombourne
what happens if ClassA is removed from dependencies by operationqueue or another controller object? I would use notifications from the operations, MYOperationDidSucceed and MYOperationDidFailWithError. Add the next operation to the queue only on success, clean up (undoManager) any mess left behind in the event of a failure
falconcreek
I'd argue that removing a dependency from an operation is telling the operation that you don't care about that result anymore! However, if you're worried about that happening notifications would work ;)
deanWombourne