【问题标题】:Competition with two methods and run completion method once两种方法竞争,一次运行完成方法
【发布时间】:2018-10-02 01:19:30
【问题描述】:

我有三个方法,其中两个同时运行。第三种方法只有在第一种方法和第二种方法一起完成它们的工作时才应该开始。无论是第一种方法还是第二种方法,竞争对手都可以先完成他们的工作。

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

【问题讨论】:

  • 为什么不使用块?这是执行此操作的标准方法。只需在您当前正在调用的地方调用您的块method3...
  • 我使用的两个服务都是基于delegate的,我改不了=(
  • 好的,将完成处理程序保存在块属性中,并让您的代表从那里调用它们。或者,将您的 dispatch_group_t 变量保存在属性中。但是调度组是跟踪一系列异步任务何时完成的最佳方式。其他选项包括NSOperation 依赖、反应模式等,但调度组是最好的。它使您摆脱了维护一堆状态属性的麻烦(您可能必须同步这些属性以使线程安全),并且它具有可扩展性。
  • 你能举一个小例子来说明如何在代表中调用组块吗?我试过了,我仍然有一个方法工作了两次。 = (
  • 在下面查看我修改后的答案,我在使用基于委托协议的 API 而不是基于块的 API 时显示了两个选项。

标签: ios objective-c cocoa


【解决方案1】:

你说:

我有三个方法,其中两个同时运行。

这意味着它们必须是异步的或在后台队列上运行(否则它们无法同时运行)。

所以,我们的想法是你应该给它们两个完成处理程序(它们完成后将被调用):

- (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 调用都由它们对应的 @ 平衡时应该做什么987654323@电话:

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];
});

在随后的 cmets 中,您提到您使用的不是基于完成块的 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");
    }
    

就个人而言,我通常倾向于第一个选项(这样,调度组的内容包含在一个方法中),但任何一种方法都有效。

【讨论】:

    【解决方案2】:

    为什么不在串行队列中将“调用”分派给 method3?

    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
    

    对 method3 的调用从不竞争。

    【讨论】:

      【解决方案3】:
      - (void)method1 {
      //DO Long Work
      isMethod2Complete = YES;
       dispatch_async(dispatch_get_main_queue(), ^(void){
      [self method3];
        }
          }
      
      - (void)method2 { … } 
      
      - (void)method3 { … } 
      

      【讨论】:

        【解决方案4】:
        - (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
            }
        }
        

        【讨论】:

        • 这样调用方法
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-03-15
        • 1970-01-01
        • 1970-01-01
        • 2018-04-06
        • 1970-01-01
        • 2011-03-09
        相关资源
        最近更新 更多