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

ars*_*ium 0 cocoa objective-c ios

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

- (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
        }
    }
Run Code Online (Sandbox Code Playgroud)

应始终调用方法3一次.但问题是有一种情况是method1和method2同时完成了工作,而method3被调用了两次.告诉我如何在iOS的目标c中解决这个问题?

更新:一个具体的例子,我有两个服务,当他们完成工作时调用代理.

- (void)method1Handler {
        isMethod1Complete = YES;
        [self method3];
}

- (void)method2Handler {
        isMethod1Complete = YES;
        [self method3];
}
Run Code Online (Sandbox Code Playgroud)

如何在没有块的情况下解决这个问题 对于街区而言,Rob的榜样是最好的.

Rob*_*Rob 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
}
Run Code Online (Sandbox Code Playgroud)

在上面的示例中,我添加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];
});
Run Code Online (Sandbox Code Playgroud)

在后续评论中,您提到您没有使用基于完成块的API,而是使用基于委托协议的API.您有几个选项,例如:

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

    例如,定义块属性:

    @property (nonatomic, copy, nullable) void (^completionOne)(void);
    @property (nonatomic, copy, nullable) void (^completionTwo)(void);
    
    Run Code Online (Sandbox Code Playgroud)

    然后,你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;
    }
    
    Run Code Online (Sandbox Code Playgroud)

    然后,委托协议完成API将调用已保存的块属性(并可能将它们重置nil为释放与这些块关联的内存).

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

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

    @property (nonatomic, strong, nullable) dispatch_group_t group;
    
    Run Code Online (Sandbox Code Playgroud)

    然后,您创建您的组并开始您的两个任务:

    self.group = dispatch_group_create();
    
    [self method1];
    
    [self method2];
    
    dispatch_group_notify(self.group, dispatch_get_main_queue(), ^{
        [self method3];
    });
    
    Run Code Online (Sandbox Code Playgroud)

    然后,这两种方法然后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");
    }
    
    Run Code Online (Sandbox Code Playgroud)

就个人而言,我通常倾向于第一个选项(这样,调度组的东西包含在一个方法中),但这两种方法都有效.