Ios 链接“NSOperation”:将一个操作的结果传递到下一个操作

Ios 链接“NSOperation”:将一个操作的结果传递到下一个操作,ios,objective-c,nsoperation,nsoperationqueue,Ios,Objective C,Nsoperation,Nsoperationqueue,我一直在寻找一种方法来传递链式NSOperation的结果。例如,假设我们有3个链接的操作: Operation1从服务器下载JSON数据 Operation2解析和建模接收到的JSON 操作3下载用户图像 所以Op3依赖于Op2,而Op2依赖于Op1。但我正在寻找从Op1->Op2传递结果的方法,然后从Op2->Op3传递结果为: [operation1 startWithURL:url]; [operation2 parseJSONfromOp1IntoModel:JSONData]; [o

我一直在寻找一种方法来传递链式
NSOperation
的结果。例如,假设我们有3个链接的操作:

  • Operation1
    从服务器下载
    JSON
    数据
  • Operation2
    解析和建模接收到的JSON
  • 操作3
    下载用户图像
  • 所以Op3依赖于Op2,而Op2依赖于Op1。但我正在寻找从Op1->Op2传递结果的方法,然后从Op2->Op3传递结果为:

    [operation1 startWithURL:url];
    [operation2 parseJSONfromOp1IntoModel:JSONData];
    [operation3 downloadUserImagesForUser: UserModelObject];
    

    嵌套块似乎不是一个清晰易读的解决方案,你知道吗?

    创建链式操作:

    从Op1的完成块中创建Op2,然后使用委托或类似的方法来设置对新创建的操作的依赖关系。您可以使用此模式链接任意数量的对象。要在完成块中传递结果,不能使用
    NSOperation
    上的
    completionBlock
    。您需要定义自己的(就像我在
    almostFinished
    中所做的那样)以便将结果传递给用户

    - (void)someMethod {
        Operation1 *operation1 = [[Operation1 alloc] init];
        operation1.almostFinished = ^(id op1Result) {
    
            Operation2 *operation2 = [[Operation2 alloc] initWithResultFromOp1: op1Result];
            operation2.almostFinished = ^(id op2Result) {
    
                Operation3 *operation3 = [[Operation3 alloc] initWithResultFromOp2:op2Result];
                operation3.completionBlock = ^{
                    NSLog(@"Operations 1 and 2 waited on me, but now we're all finished!!!);
                };
    
                [operation2 addDependency:operation3];
                [queue addOperation:operation3];
            };
    
            [operation1 addDependency:operation2];
            [queue addOperation:operation2];
        };
    
        [queue addOperation:operation1];
    }
    
    自定义子类

    您需要将
    NSOperation
    子类化才能使其工作。正如我提到的,您需要定义自己的完成块,并确保在操作真正完成之前调用完成块,以便添加依赖项。您可以在不同的块或委托方法中添加依赖项,而不是在新的完成块中添加依赖项。这种方式使我的例子简洁明了

    @interface Operation: NSOperation {
    @property (nonatomic, copy) void (^almostFinished)(id result);
    @end
    
    @implementation Operation {
        //...
    
    - (void)main {
        //...
        // Call here to allow to add dependencies and new ops
        self.almostFinished(result);  
    
        // Finish the op
        [self willChangeValueForKey:@"isFinished"];
        // repeat for isExecuting and do whatever else
        [self didChangeValueForKey:@"isFinished"];
    }
    @end
    

    编辑:这不是最可读的东西,但它包含一个方法中的所有代码。如果你想变得有趣,那么就把事情放在委托方法中,或者创造性地定义这些事情。

    如果你想链接操作,但不喜欢嵌套,你可以使用
    NSOperation
    子类,然后定义你自己的完成处理程序:

    DownloadOperation *downloadOperation = [[DownloadOperation alloc] initWithURL:url];
    ParseOperation *parseOperation = [[ParseOperation alloc] init];
    DownloadImagesOperation *downloadImagesOperation = [[DownloadImagesOperation alloc] init];
    
    downloadOperation.downloadCompletionHandler = ^(NSData *data, NSError *error) {
        if (error != nil) {
            NSLog(@"%@", error);
            return;
        }
    
        parseOperation.data = data;
        [queue addOperation:parseOperation];
    };
    
    parseOperation.parseCompletionHandler = ^(NSDictionary *dictionary, NSError *error) {
        if (error != nil) {
            NSLog(@"%@", error);
            return;
        }
    
        NSArray *images = ...;
    
        downloadImagesOperation.images = images;
        [queue addOperation:downloadImagesOperation];
    };
    
    [queue addOperation:downloadOperation];
    
    坦白地说,我不确定这是否比嵌套方法更直观:

    DownloadOperation *downloadOperation = [[DownloadOperation alloc] initWithURL:url downloadCompletionHandler:^(NSData *data, NSError *error) {
        if (error != nil) {
            NSLog(@"%@", error);
            return;
        }
    
        ParseOperation *parseOperation = [[ParseOperation alloc] initWithURL:data parseCompletionHandler:^(NSDictionary *dictionary, NSError *error) {
            if (error != nil) {
                NSLog(@"%@", error);
                return;
            }
    
            NSArray *images = ...
    
            DownloadImagesOperation *downloadImagesOperation = [[DownloadImagesOperation alloc] initWithImages:images imageDownloadCompletionHandler:^(NSError *error) {
                if (error != nil) {
                    NSLog(@"%@", error);
                    return;
                }
    
                // everything OK
            }];
            [queue addOperation:downloadImagesOperation];
        }];
        [queue addOperation:parseOperation];
    }];
    [queue addOperation:downloadOperation];
    

    顺便说一下,以上假设您熟悉子类化
    NSOperation
    ,特别是创建异步
    NSOperation
    子类(以及执行所有必要的KVO)的微妙之处。如果你需要这方面的例子,请告诉我

    您是否尝试过使用
    completionBlock
    ?您是否看到我找到了一个用于执行这种排序的有用库。遗憾的是,使用自定义完成处理程序嵌套块是典型的
    NSOperation
    方法。或者看看承诺/未来,例如或。@Rob,谢谢,但出于某些原因,我必须坚持使用SDK而不是第三方库,否则我会用不到8行代码来完成:)谢谢@keithbhunter,我现在尝试子类化。我想说第一种方法看起来正确,但有一个小错误。配置每个操作后,应设置依赖项,然后将所有3个操作添加到队列中,而不是在每个操作结束后。如果返回任何错误,则取消队列。是的,这是另一种模式。我故意省略了它,因为它有点易碎。当您有操作依赖项时,这不一定应用它们的完成处理程序,它们可以在不同的线程上异步运行。因此,如果要在完成处理程序中传递数据,则在操作完成之前,
    NSOperation
    子类必须同步调用其完成处理程序。但是,让代码完整性取决于另一个类的实现细节是值得怀疑的设计。上述选项完全依赖于更健壮的公共接口。