我有几种方法具有以下结构:
- (void) doSomethingWithCompletion: (void (^)(NSError *error)) completion {
__block NSError *fetchError = nil;
dispatch_group_t dispatchGroup = dispatch_group_create();
for (Item* item in self.items)
{
dispatch_group_enter(dispatchGroup);
// fetchError = fetch online data
}
dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(),^{
if (completion)
completion(fetchError);
});
}
我的目标是在彼此之后运行几个doSomethings,所以我可以这样:
[self doSomethingAWithCompletion: ^(NSArray *results NSError *error) {
if (error == nil) {
[self doSomethingBWithArray: results withCompletion: ^(NSError *error) {
if (error == nil) {
[self doSomethingCWithCompletion: ^(NSError *error) {
if (error == nil) {
// done!!
}
}];
}];
}];
我正在努力的是第二个代码块(没有双关语);是嵌套所有方法,还是有其他解决方案?
重要的是,doSomethingBWithCompletion
无法在doSomethingAWithCompletion
完成之前开始,doSomethingCWithCompletion
需要等到doSomethingBWithCompletion
完成等等。
此外,doSomethingBWithCompletion
使用doSomethingAWithCompletion
等生成的数据
编辑:经过大量的思考,重构和简化我的代码之后,我最终只能使用嵌套方法完成了两个函数,如上所述,并使用{{1结果数组。
答案 0 :(得分:1)
重要的是,doSomethingBWithCompletion在doSomethingAWithCompletion完成之前无法启动,doSomethingCWithCompletion需要等到doSomethingBWithCompletion完成等等。
根据评论:
块的结果不依赖于第一个结果不是吗?
并且
是的。例如,在第一个doSomething我确定哪些项目已过时,在第二个doSomething我下载并解析更新的项目,在第三个doSomething我将它们保存到商店。
(顺便说一下:你真的应该把这些信息添加到你的Q中。)
如果某个操作取决于上一个操作的结果(不仅是执行),则必须嵌套这些块。您的代码看起来不像这样,因为没有数据传递给完成块。
如果您没有这种依赖关系,则可以使用专用串行调度队列。但是,如果你有一个类似于持有从块到块传递的数据的管理器类,那么这也是你的情况下的解决方案。但这似乎是高度反感受的。
答案 1 :(得分:1)
社区可能会尝试将promises添加到objective-c中,这样会很高兴,因为这就是这里所需要的。如果没有提交到一个全新的库,你可以通过递归执行异步任务来处理嵌套(我同意这是一个无聊的事情......对于你的示例代码,这样的事情是这样的:
从没有参数并且产生数组的操作开始......
- (void)firstOpWithCompletion:(void (^)(NSArray *, NSError *))completion {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSArray *components = [@"this is an array of strings from the FIRST op" componentsSeparatedByString:@" "];
if (completion) {
completion(components, nil);
}
});
}
以下是一对采用数组参数并生成数组...
- (void)secondOpWithParam:(NSArray *)array completion:(void (^)(NSArray *, NSError *))completion {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
if (completion) {
NSArray *components = [@"these strings are from the SECOND op" componentsSeparatedByString:@" "];
NSArray *result = [array arrayByAddingObjectsFromArray:components];
if (completion) {
completion(result, nil);
}
}
});
}
- (void)thirdOpWithParam:(NSArray *)array completion:(void (^)(NSArray *, NSError *))completion {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
if (completion) {
NSArray *components = [@"these strings are from the THIRD op" componentsSeparatedByString:@" "];
NSArray *result = [array arrayByAddingObjectsFromArray:components];
if (completion) {
NSLog(@"we did it. returning %@", result);
completion(result, nil);
}
}
});
}
// ...as many as these as you need
现在,正如我在编辑之前的回答一样,我们最初只是在中间调用中添加一个参数传递...
- (void)doSeveralThingsInSequence:(NSArray *)todo param:(NSArray *)param {
if (todo.count == 0) return;
// you could generalize further here, by passing a "final" block and run that before the return
NSString *nextTodo = todo[0];
SEL sel = NSSelectorFromString(nextTodo);
IMP imp = [self methodForSelector:sel];
void (*func)(id, SEL, NSArray *, void (^)(NSArray *, NSError *)) = (void *)imp;
func(self, sel, param, ^(NSArray *result, NSError *error) {
if (!error) {
NSArray *remainingTodo = [todo subarrayWithRange:NSMakeRange(1, todo.count-1)];
[self doSeveralThingsInSequence:remainingTodo param:result];
}
});
}
单步执行代码:如果没有任何操作,此方法会失效,否则它会从传递的数组中获取下一个选择器名称,获取它的C函数实现并调用它,在启动的调用堆栈上放置一个完成块其余选择器的过程结束。
最后,doEverything调用第一个操作开始,然后开始运行一个操作列表(可以是一个任意长的列表),将数组输出从一个作为数组输入传递给下一个。 (您可以通过在链中传递id
来进一步概括这一点
- (void)doEverything {
[self firstOpWithCompletion:^(NSArray *array, NSError *error) {
NSArray *todo = @[ @"secondOpWithParam:completion:", @"thirdOpWithParam:completion:" ];
[self doSeveralThingsInSequence:todo param:array];
}];
}
我完全按照发布的方式对此进行了测试,并看到了预期的输出:
(
this,
is,
an,
array,
of,
strings,
from,
the,
FIRST,
op,
these,
strings,
are,
from,
the,
SECOND,
op,
these,
strings,
are,
from,
the,
THIRD,
op
)