【问题标题】:GCD: asynchronously wait for conditionGCD:异步等待条件
【发布时间】:2013-10-05 13:35:38
【问题描述】:

在我的 iOS 应用程序中,我想在更新 UI 之前等待条件变为真。 我是这样做的:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
  while (!condition) NSLog("waiting for the condition");
  dispatch_sync(dispatch_get_main_queue, ^{
    //update the UI here
  });
});

上面的代码工作得很好,但是我想问一下使用 while 循环来做等待工作是否好,以及是否有更好的方法。 谢谢!

---更新

条件实际上是 4 个 BOOL 变量的组合。每个变量都与来自服务器的内容请求相关联。我正在使用 AFNetworking 框架。在 4 个请求的每一个的完成块中,我将关联的 BOOL 变量设置为 YES。 所以,实际的while循环是这样的:

while (!([MyRequest request1].isFinished && [MyRequest request2].isFinished && [MyRequest request3].isFinished && [MyRequest request4].isFinished)) NSLog("waiting for the condition");

【问题讨论】:

  • 你能给我们一个不那么抽象的例子吗?您要检查的 condition 到底是什么?不同的场景建议使用不同的方法(信号量、KVO、重复NSTimerCADisplayLink 等)。但是这种while 循环方法通常并不可取。
  • 我只是想简化问题,但这让我的问题变得更糟。只需更新详细信息,@Rob。
  • 这很有帮助。在这种情况下,我只需创建一个新操作,并使其依赖于其他四个请求操作。请参阅修订后的答案。 (仅供参考,这有效地对这四个操作的 isFinished 属性执行 KVO。)

标签: ios asynchronous grand-central-dispatch


【解决方案1】:

在修订后的问题中,听起来您有四个要依赖的 AFNetworking 操作。这要容易得多。您可以只添加一个新操作,并使其依赖于其他四个操作:

NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        // update UI
    }];
}];

[operation addDependency:requestOneOperation];
[operation addDependency:requestTwoOperation];
[operation addDependency:requestThreeOperation];
[operation addDependency:requestFourOperation];

[queue addOperation:operation];

addDependency 机制本质上为您执行其他四个操作中的每一个的isFinished 的 KVO。这是使用像 AFNetworking 这样的基于NSOperation 的框架的乐趣之一。这种依赖真的很容易做到。


原答案:

如果你必须这样做,你可能会使用一个信号量,例如,你会创建一个信号量:

dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

你会让你的异步块等待:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
    dispatch_semaphore_wait(semaphore);
    dispatch_sync(dispatch_get_main_queue, ^{
        //update the UI here
    });
});

当条件满足时,原本设置此条件标志的代码将改为:

dispatch_semaphore_signal(semaphore);

话虽如此,除非绝对必要,否则我宁愿不要看到这样的队列阻塞(甚至是并发全局队列)。如果其他代码可以发出信号量信号,我不确定它为什么不能自己启动 UI 更新。如果我确实使用了这种信号量技术,至少我会让这个等待过程发生在我自己创建的队列上,而不是全局队列上。


您可以在许多情况下使用并且我可能更喜欢的另一种方法是使用key value observing

例如,我可以观察到一个名为obj 的对象的someProperty 属性的变化,如下所示:

[obj addObserver:self forKeyPath:@"someProperty" options:NSKeyValueObservingOptionNew context:NULL];

然后我会实现observeValueForKeyPath:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if ([keyPath isEqualToString:@"someProperty"])
    {
        NSLog(@"update UI here");
    }
}

每当我的obj 对象的someProperty 属性更新时,我的observeValueForKeyPath 方法就会被调用。

仅供参考,我还要确保在此对象被释放之前,我会删除 obj 的观察者:

[obj removeObserver:self forKeyPath:@"someProperty"];

显然,这假定somePropertyKey Value Coding Compliant。但如果是的话,这是一项很棒的技术。

【讨论】:

  • 假设条件在我无法访问的地方更新,因此我无法使用 dispatch_semaphore_signal 发送信号。
  • 是最后一条语句中的'queue' [queue addOperation:operation]; [NSOperationQueue mainQueue]?
  • @ThanhPham 绝对不是主队列。这是您创建的queue,例如NSOperationQueue *queue = [[NSOperationQueue alloc] init];。可能是您将其他四个请求添加到的同一队列。随心所欲。
【解决方案2】:

虽然这里的一般模式是正确的(Apple 将其称为“调用回调”),但while(!condition) 位也称为“自旋锁”,绝对不是等待条件的最佳方式。考虑改用NSTimerNSRunLoop

【讨论】:

  • 是这样的吗? while (!condition && [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distinctFuture]]);所以 while 语句仍然存在,但我想由于 NSRunLoop 部分,它不再是自旋锁了吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-10
  • 1970-01-01
  • 2023-03-12
  • 1970-01-01
相关资源
最近更新 更多