我有三种方法,其中两种同时运行。第三种方法只有在第一种和第二种方法共同完成工作时才能启动。竞争对手的第一种或第二种方法可以先完成工作。
- (void)method1 {
//DO Long Work
isMethod1Complete = YES;
[self method3];
}
- (void)method2 {
//DO Long Work
isMethod2Complete = YES;
[self method3];
}
- (void)method3 {
if (isMethod1Complete && isMethod2Complete) {
//DO Work once
}
}
方法3应始终调用一次。但问题是有一种情况是method1和method2同时完成了工作,而method3被调用了两次。告诉我如何在iOS的目标c中解决这个问题?
更新:一个具体的例子,我有两个服务在完成工作时调用代理。
- (void)method1Handler {
isMethod1Complete = YES;
[self method3];
}
- (void)method2Handler {
isMethod1Complete = YES;
[self method3];
}
如何在没有积木的情况下解决这个问题? 对于块,Rob的例子是最好的。
答案 0 :(得分:6)
你说:
我有三种方法,其中两种同时运行。
这意味着它们必须是异步的或在后台队列上运行(否则它们无法同时运行)。
所以,我们的想法是你应该给他们两个完成处理程序(在完成后会调用它们):
- (void)method1WithCompletion:(void(^ _Nonnull)(void))completion {
dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{
//DO Long Work asynchronously
completion();
});
}
- (void)method2WithCompletion:(void(^ _Nonnull)(void))completion {
dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{
//DO Long Work asynchronously
completion();
});
}
- (void)method3 {
// final task
}
在上面的示例中,我向后台队列添加了显式dispatch_async
调用,以确保两个长任务异步运行。但是如果代码已经在异步(例如网络请求),那么您可能不需要这些dispatch_async
调用,只需将completion()
调用放在由您提供的任何API提供的完成处理程序中已经在用但是,如果没有关于method1
和method2
正在做什么的更多信息,我就不能更具体了。
但是,除此之外,在method1
和method2
拥有自己的完成处理程序之后,您可以使用dispatch_group_notify
来确定在dispatch_group_enter
时应该执行的操作}调用由相应的dispatch_group_leave
调用进行平衡:
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
[self method1WithCompletion:^{
dispatch_group_leave(group);
}];
dispatch_group_enter(group);
[self method2WithCompletion:^{
dispatch_group_leave(group);
}];
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
[self method3];
});
在后续评论中,您提到您没有使用基于完成块的API,而是使用基于委托协议的API。您有几个选项,例如:
您可以使用相同的上述闭包模式,但只需将完成处理程序保存为块属性,例如:
例如,定义块属性:
@property (nonatomic, copy, nullable) void (^completionOne)(void);
@property (nonatomic, copy, nullable) void (^completionTwo)(void);
然后,您的method1
和method2
会保存这些块:
- (void)method1WithCompletion:(void(^ _Nonnull)(void))completion {
self.completionOne = completion;
// start your time consuming asynchronous process
}
// and your completion delegate method can then call the saved closure
// and then remove it
- (void)method1DidComplete {
self.completionOne();
self.completionOne = nil;
}
- (void)method2WithCompletion:(void(^ _Nonnull)(void))completion {
self.completionTwo = completion;
// start second asynchronous process
}
// same as above
- (void)method2DidComplete {
self.completionTwo();
self.completionTwo = nil;
}
委托协议完成API然后只调用保存的块属性(并可能将它们重置为nil
以释放与这些块相关联的内存)。
然后您可以使用上面原始答案中显示的调度组通知流程。
或者,您可以单独使用调度组,而不是使用块。例如,定义调度组属性:
@property (nonatomic, strong, nullable) dispatch_group_t group;
然后,您创建组并开始执行两项任务:
self.group = dispatch_group_create();
[self method1];
[self method2];
dispatch_group_notify(self.group, dispatch_get_main_queue(), ^{
[self method3];
});
然后,当你在各自的完成处理程序委托方法中启动任务和dispatch_group_enter
时,这两个方法dispatch_group_leave
:
- (void)method1 {
dispatch_group_enter(self.group);
// start first asynchronous process
}
// in your delegate completion method, you "leave" the group
- (void)method1DidComplete {
dispatch_group_leave(self.group);
}
- (void)method2 {
dispatch_group_enter(self.group);
// start second asynchronous process
}
- (void)method2DidComplete {
dispatch_group_leave(self.group);
}
- (void)method3 {
// you might as well remove the group now that you're done with it
self.group = nil;
// final task
NSLog(@"doing three");
}
就个人而言,我通常倾向于第一个选项(这样,调度组的东西包含在一个方法中),但这两种方法都有效。
答案 1 :(得分:1)
为什么不在串行队列中调度方法3的“调用”?
dispatch_queue_t notSimQ;
notSimQ = dispatch_queue_create("notSimQ", NULL);
- (void)method1 {
//DO Long Work
isMethod1Complete = YES;
dispatch_async( notSimQ, // or sync
^{
[self method3];
});
}
- (void)method2 { … } // similiasr
- (void)method3 { … } // unchanged
对方法3的调用永远不会参与竞争。
答案 2 :(得分:1)
- (void)method1 {
//DO Long Work
isMethod2Complete = YES;
dispatch_async(dispatch_get_main_queue(), ^(void){
[self method3];
}
}
- (void)method2 { … }
- (void)method3 { … }
答案 3 :(得分:-2)
- (void)viewDidLoad {
[super viewDidLoad];
[self method1];
[self method2];
}
- (void)method1 {
//DO Long Work
isMethod1Complete = YES;
}
- (void)method2 {
//DO Long Work
isMethod2Complete = YES;
dispatch_async(dispatch_get_main_queue(), ^(void){
[self method3];
}
}
- (void)method3 {
if (isMethod1Complete && isMethod2Complete) {
//DO Work once
}
}