链接`NSOperation`:将操作的结果传递给下一个操作

时间:2015-11-10 15:43:04

标签: ios objective-c nsoperation nsoperationqueue

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

  1. Operation1从服务器下载JSON数据
  2. Operation2解析&模型JSON收到
  3. Operation3下载用户图片
  4. 因此Op3将依赖于Op2,而Op2依赖于Op1。但我正在寻找从Op1传递结果的方法 - > Op2,然后来自Op2 - > Op3为:

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

    和嵌套块似乎不是一个干净的可读解决方案,任何想法?

2 个答案:

答案 0 :(得分:4)

如果要链接操作但不喜欢嵌套,可以使用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)的细微之处。如果您需要如何完成的示例,请告诉我。

答案 1 :(得分:2)

创建链式操作:

从Op1的完成块内创建Op2,然后使用委托或类似的东西来设置对新创建的操作的依赖性。您可以使用此模式链接任意数量的链接。要在完成块中传递结果,您无法使用completionBlock上的NSOperation。你需要定义自己的(就像我对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

编辑:这不是最易读的东西,但它包含一种方法中的所有代码。如果你想获得幻想,那就把事情放在委托方法中,或者用你如何定义这些东西来创造。