用两种方法比赛并运行一次完成方法

时间:2018-04-21 14:07:50

标签: ios objective-c cocoa

我有三种方法,其中两种同时运行。第三种方法只有在第一种和第二种方法共同完成工作时才能启动。竞争对手的第一种或第二种方法可以先完成工作。

- (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的例子是最好的。

4 个答案:

答案 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提供的完成处理程序中已经在用但是,如果没有关于method1method2正在做什么的更多信息,我就不能更具体了。

但是,除此之外,在method1method2拥有自己的完成处理程序之后,您可以使用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。您有几个选项,例如:

  1. 您可以使用相同的上述闭包模式,但只需将完成处理程序保存为块属性,例如:

    例如,定义块属性:

    @property (nonatomic, copy, nullable) void (^completionOne)(void);
    @property (nonatomic, copy, nullable) void (^completionTwo)(void);
    

    然后,您的method1method2会保存这些块:

    - (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以释放与这些块相关联的内存)。

    然后您可以使用上面原始答案中显示的调度组通知流程。

  2. 或者,您可以单独使用调度组,而不是使用块。例如,定义调度组属性:

    @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");
    }
    
  3. 就个人而言,我通常倾向于第一个选项(这样,调度组的东西包含在一个方法中),但这两种方法都有效。

答案 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
    }
}