1
votes

I extend NSOperation (call it A) which contains NSOperationQueue for other NSOperations (which is another extended class different from A, call these operations B). When operation A is running (executing B operations) how do i call a specific function/method on operation A when certain event takes place on B operations? For example every operation B that finishes it calls a function on operation A returning itself?

*Nested NSOperation and NSOperationQueue(s)

Hope this mockup pseudo code can help to draw the picture.

//My classes extended from NSOperation

NSOperation ClassA

NSOperation ClassB

//MainApp

-(void)applicationDidFinishLaunching:(NSNotification *)aNotification {

    ClassA A1;

    ClassA A2;

    NSOperationQueue Queue;
    Queue AddOperation: A1;
    Queue AddOperation: A2;


}

//Main of ClassA

-(void)main {

     ClassB B1;

     ClassB B2;

     NSOperationQueue Queue;
     Queue AddOperation: B1;
     Queue AddOperation: B2;

}

//Main of ClassB

-(void)main {

   //Do some work and when done call selector on ClassA above

}
2
Sounds convoluted and terrible. What are you trying to do?Dave DeLong
Some pseudo-code might make the problem more clear.Georg Fritzsche
@Dave DeLong: This is a coding experiment. The application takes number of URLs which point to a download file on a server. NSOperation is created for each URL, size of the file is determined and split up into chunks. From there each chunk of the file is downloaded by separate NSOperation. Does that make sense.MikeM
I call it nested NSOperations, I have the same problem.kubbing

2 Answers

1
votes

NSOperation has a mechanism to register dependencies. See NSOperation's documentation and addDependency:.

Let's split the work your A does: AX: spawning Bs, and AY: collecting the data in B. I think it's better to design B so that it can be used without A. Let B just download the data and hold it in the property data, as in

@interface B:NSOperation{
}
-initWithURL:(NSURL*)url atPosition:(NSUInteger)position;
@property (readonly)NSData* data;
@end;

A doesn't have to have its own queue, one per object. Generating B's doesn't take much time either, so AX doesn't have to be an operation.

So, I would just define the operations AY and B, prepare just one queue, and perform something like

NSOperationQueue* queue=...;
NSArray* urls = ... ;
for(NSURL* url in urls){
     AY* ay=[[AY alloc] init];
     for(chunks in url){
         B* b= an instance for B for the chunk in url
         [ay addDependency:b];
         [queue addOperation:b];
     } 
     [queue addOperation:ay];
}

Then, in AY's main, you can

-main{
      NSArray*bs=[self dependencies];
      for(B*b in bs){
           NSData* data=b.data;
      }
}

Note that, thanks to the dependencies, it's guaranteed that when AY is run, all of the B's has finished its operation. This way, the implementation of B is independent of A.

0
votes

I've written a class called MXSuperOperation to nest NSOperations for the app I'm currently working on. I've posted it here:

https://gist.github.com/joerick/e4d2c99a2127715d9bc3

Subclass MXSuperOperation and add suboperations in the -mainError: method.

@interface OperationA : MXSuperOperation

@end

@implementation OperationA

- (BOOL)mainError:(NSError **)error
{
    [self addSuboperation:[OperationB new]];
    [self addSuboperation:[OperationB new]];

    return YES;
}

@end

Then to run something at the end of all the OperationBs, you can either add a completionBlock to operationA:

[operationA setCompletionBlockWithSuccess:^(id operation) {
    NSLog(@"success");
} failure:^(id operation, NSError *error) {
    NSLog(@"error %@", error);
}];

Or you can implement -endError: in OperationA (depending on how you want to structure the code).

Note that this class is built on an NSOperation subclass I made called MXCheckedOperation, which just codifies success/failure and error reporting for NSOperations.