ios – 管理一堆具有依赖关系的NSOperation

前端之家收集整理的这篇文章主要介绍了ios – 管理一堆具有依赖关系的NSOperation前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我正在开发一个创建内容并将其发送到现有后端的应用程序.内容标题,图片和位置.没有什么花哨

后台有点复杂,所以这里是我要做的:

>让用户拍照,输入标题并授权地图使用其位置
>为帖子生成一个唯一的标识符
>在后台创建帖子
>上传图片
>刷新UI

我使用了几个NSOperation子类来实现这个工作,但我不为我的代码感到骄傲,这里是一个例子.

NSOperation *process = [NSBlockOperation blockOperationWithBlock:^{
    // Process image before upload
}];

NSOperation *filename = [[NSInvocationOperation alloc] initWithTarget: self selector: @selector(generateFilename) object: nil];

NSOperation *generateEntry = [[NSInvocationOperation alloc] initWithTarget: self selector: @selector(createEntry) object: nil];

NSOperation *uploadImage = [[NSInvocationOperation alloc] initWithTarget: self selector: @selector(uploadImageToCreatedEntry) object: nil];

NSOperation *refresh = [NSBlockOperation blockOperationWithBlock:^{
    // Update UI
    [SVProgressHUD showSuccessWithStatus: NSLocalizedString(@"Success!",@"Success HUD message")];
}];

[refresh addDependency: uploadImage];

[uploadImage addDependency: generateEntry];
[generateEntry addDependency: filename];
[generateEntry addDependency: process];

[[NSOperationQueue mainQueue] addOperation: refresh];
[_queue addOperations: @[uploadImage,generateEntry,filename,process] waitUntilFinished: NO];

以下是我不喜欢的东西:

>在我的createEntry中:例如,我将生成文件名存储在一个属性中,它与我的类的全局范围
>在uploadImageToCreatedEntry:方法中,我使用dispatch_async dispatch_get_main_queue()来更新我的HUD中的消息
>等

你如何管理这样的工作流?我想避免嵌入多个完成块,我觉得NSOperation真的是要走的路,但我也觉得在某个地方有更好的实现.

谢谢!

解决方法

您可以使用 ReactiveCocoa
完成这个很容易.其中一个大目标就是制造这样的一个
构成微不足道

如果您以前没有听说过ReactiveCocoa,或者不熟悉它,请检查
Introduction
为了快速解释.

我将避免在这里重复整个框架概述,但足以说出来
RAC实际上提供了一个承诺/期货的超集.它允许你组合和
完全不同起源的转换事件(UI,网络,数据库,KVO,
通知等),这是非常强大的.

要开始RACify这个代码,我们可以做的第一个也是最简单的事情
这些单独的操作变成方法,并确保每个操作都返回
RAC信号.这并不是绝对必要的(它们都可以在其中定义
一个范围),但它使代码更加模块化和可读性.

例如,让我们创建一个对应的进程和对应的信号
generateFilename:

- (RACSignal *)processImage:(UIImage *)image {
    return [RACSignal startEagerlyWithScheduler:[RACScheduler scheduler] block:^(id<RACSubscriber> subscriber) {
        // Process image before upload

        UIImage *processedImage = …;
        [subscriber sendNext:processedImage];
        [subscriber sendCompleted];
    }];
}

- (RACSignal *)generateFilename {
    return [RACSignal startEagerlyWithScheduler:[RACScheduler scheduler] block:^(id<RACSubscriber> subscriber) {
        NSString *filename = [self generateFilename];
        [subscriber sendNext:filename];
        [subscriber sendCompleted];
    }];
}

其他操作(createEntry和uploadImageToCreatedEntry)将非常相似.

一旦我们有了这些,就很容易组成他们并表达他们的意思
依赖关系(虽然注释使它看起来有点密集):

[[[[[[self
    generateFilename]
    flattenMap:^(NSString *filename) {
        // Returns a signal representing the entry creation.
        // We assume that this will eventually send an `Entry` object.
        return [self createEntryWithFilename:filename];
    }]
    // Combine the value with that returned by `-processImage:`.
    zipWith:[self processImage:startingImage]]
    flattenMap:^(RACTuple *entryAndImage) {
        // Here,we unpack the zipped values then return a single object,// which is just a signal representing the upload.
        return [self uploadImage:entryAndImage[1] toCreatedEntry:entryAndImage[0]];
    }]
    // Make sure that the next code runs on the main thread.
    deliverOn:RACScheduler.mainThreadScheduler]
    subscribeError:^(NSError *error) {
        // Any errors will trickle down into this block,where we can
        // display them.
        [self presentError:error];
    } completed:^{
        // Update UI
        [SVProgressHUD showSuccessWithStatus: NSLocalizedString(@"Success!",@"Success HUD message")];
    }];

请注意,我重命名了一些方法,以便他们可以接受输入
他们的依赖,给我们一个更自然的方式来从一个价值观
操作到下一个

这里有很大的优势:

>您可以自上而下阅读,所以很容易理解这个顺序
事情发生在哪里,依赖性在何处.
>在不同的线程之间移动工作非常容易,如下所示
使用-deliverOn :.
>任何这些方法发送的任何错误自动取消所有这些
其余的工作,最终达到subscribeError:block容易
处理.
>你也可以用其他事件流(即不只是)来组合
操作).例如,您可以将其设置为仅在UI时触发
信号(如按钮点击)触发.

ReactiveCocoa是一个巨大的框架,不幸的是很难蒸馏
优点下降到一个小的代码示例.我强烈建议您查看
when to use ReactiveCocoa的例子了解更多关于如何帮助.

猜你在找的iOS相关文章