ios - 在 Objective-C 中序列化异步任务

标签 ios objective-c multithreading nsoperation

我希望能够序列化“真正的”异步方法,例如:

  • 发起网络请求
  • 显示一个 UIAlertView

这通常是一项棘手的工作,大多数串行队列示例在 NSBlockOperation 的 block 中显示“ sleep ”。这是行不通的,因为操作只有在回调发生时才完成。

我已经尝试通过子类化 NSOperation 来实现它,这里是实现中最有趣的部分:

+ (MYOperation *)operationWithBlock:(CompleteBlock)block
{
    MYOperation *operation = [[MYOperation alloc] init];
    operation.block = block;
    return operation;
}

- (void)start
{
    [self willChangeValueForKey:@"isExecuting"];
    self.executing = YES;
    [self didChangeValueForKey:@"isExecuting"];
    if (self.block) {
        self.block(self);
    }
}

- (void)finish
{
    [self willChangeValueForKey:@"isExecuting"];
    [self willChangeValueForKey:@"isFinished"];
    self.executing = NO;
    self.finished = YES;
    [self didChangeValueForKey:@"isExecuting"];
    [self didChangeValueForKey:@"isFinished"];
}

- (BOOL)isFinished
{
    return self.finished;
}

- (BOOL) isExecuting
{
    return self.executing;
}

效果很好,这是一个演示...

NSOperationQueue *q = [[NSOperationQueue alloc] init];
q.maxConcurrentOperationCount = 1;

dispatch_queue_t queue = dispatch_queue_create("1", NULL);
dispatch_queue_t queue2 = dispatch_queue_create("2", NULL);

MYOperation *op = [MYOperation operationWithBlock:^(MYOperation *o) {
    NSLog(@"1...");
    dispatch_async(queue, ^{
        [NSThread sleepForTimeInterval:2];
        NSLog(@"1");
        [o finish]; // this signals we're done
    });
}];

MYOperation *op2 = [MYOperation operationWithBlock:^(MYOperation *o) {
    NSLog(@"2...");
    dispatch_async(queue2, ^{
        [NSThread sleepForTimeInterval:2];
        NSLog(@"2");
        [o finish]; // this signals we're done
    });
}];

[q addOperations:@[op, op2] waitUntilFinished:YES];

[NSThread sleepForTimeInterval:5];

请注意,我还使用了 sleep ,但确保它们在后台线程中执行以模拟网络调用。日志内容如下

1...
1
2...
2

如愿以偿。这种方法有什么问题?有什么我应该注意的注意事项吗?

最佳答案

“序列化”异步任务实际上将被命名为“延续”(另请参阅此 wiki 文章 Continuation

假设,您的任务可以定义为带有完成处理程序的异步函数/方法,其参数是异步任务的最终结果,例如:

typedef void(^completion_handler_t)(id result);

-(void) webRequestWithCompletion:(completion_handler_t)completionHandler;
-(void) showAlertViewWithResult:(id)result completion:(completion_handler_t)completionHandler;

拥有blocks可用,通过从上一个任务的完成 block 中调用下一个异步任务可以轻松完成“继续”:

- (void) foo 
{
    [self webRequestWithCompletion:^(id result) {  
        [self showAlertViewWithResult:result completion:^(id userAnswer) {
            NSLog(@"User answered with: %@", userAnswer);
        }
    }
}

注意方法 foo被“异步”感染;)

也就是说,这里是方法的最终效果foo ,即将用户的答案打印到控制台,实际上也是异步的。

但是,“链接”多个异步任务,即“继续”多个异步任务,可能会很快变得笨拙:

使用完成 block 实现“延续”将增加每个任务的完成处理程序的缩进。此外,实现一种让用户在任何状态下取消任务的方法,并实现处理错误情况的代码,代码很快就会变得困惑,难以编写和理解。

实现“继续”以及取消和错误处理的更好方法是使用 Futures or Promises 的概念。 . Future 或 Promise 表示异步任务的最终结果。基本上,这只是一种向调用站点“发出最终结果信号”的不同方法。

在 Objective-C 中,“Promise”可以作为普通类来实现。有第三方图书馆实现“ promise ”。以下代码使用了特定的实现,RXPromise

在使用这样的Promise 时,您可以按如下方式定义您的任务:

-(Promise*) webRequestWithCompletion;
-(Promise*) showAlertViewWithResult:(id)result;

注意:没有完成处理程序。

使用Promise,异步任务的“结果”将通过“成功”或“错误”处理程序获得,该处理程序将“注册”到then。 promise 的属性(property)。成功或错误处理程序在任务完成时被调用:当它成功完成时,成功处理程序将被调用,将其结果传递给成功处理程序的参数 result。否则,当任务失败时,它会将原因传递给错误处理程序——通常是 NSError。对象。

Promise 的基本用法如下:

Promise* promise = [self asyncTasks];
// register handler blocks with "then":
Promise* handlerPromise = promise.then( <success handler block>, <error handler block> );

成功处理程序 block 有一个类型为 id 的参数 result .错误处理程序 block 有一个类型为 NSError 的参数.

注意语句promise.then(...)返回一个代表任一处理程序结果的 promise ,当“父” promise 已成功或错误地解决时调用它。处理程序的返回值可以是“立即结果”(某个对象)或“最终结果”——表示为 Promise 对象。

以下代码片段(包括复杂的错误处理)显示了 OP 问题的注释示例:

- (void) foo 
{
    [self webRequestWithCompletion] // returns a "Promise" object which has a property "then"
    // when the task finished, then:
    .then(^id(id result) {
        // on succeess:
        // param "result" is the result of method "webRequestWithCompletion"
        return [self showAlertViewWithResult:result];  // note: returns a promise
    }, nil /*error handler not defined, fall through to the next defined error handler */ )       
    // when either of the previous handler finished, then:
    .then(^id(id userAnswer) {
        NSLog(@"User answered with: %@", userAnswer);
        return nil;  // handler's result not used, thus nil.
    }, nil)
    // when either of the previous handler finished, then:
    .then(nil /*success handler not defined*/, 
    ^id(NEError* error) {
         // on error
         // Error handler. Last error handler catches all errors.
         // That is, either a web request error or perhaps the user cancelled (which results in rejecting the promise with a "User Cancelled" error)
         return nil;  // result of this error handler not used anywhere.
    });

}

代码当然需要更多的解释。有关详细和更全面的描述,以及如何在任何时间点完成取消,您可以查看 RXPromise library - 实现“Promise”的 Objective-C 类。披露:我是 RXPromise 库的作者。

关于ios - 在 Objective-C 中序列化异步任务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18202952/

相关文章:

iphone - 在iphone sdk中将文件夹从资源复制到文档

html - iOS 8 网络应用程序的启动图像

ios - 带有 numberOfLines 和 lineBreakMode 的 UILabel

ios - 应用程序在第二点后卡住,Objective C

c# - BlockingCollection.TakeFromAny 方法是否适合构建阻塞优先级队列?

python - 使用多处理/线程将 numpy 数组操作分解为 block

ios - native iOS 应用程序中的 PayPal Express Checkout

ios - 如何在代码之外使用 Xcode 4 和 iOS 4.3 将属性分配给自定义 UIControl?

ios - Plist 从 URL 到 NSMutableArray

java - 区间锁实现