等待计时器或条件成为真,然后处理代码? (等待iOS相机调整焦距)

时间:2013-03-23 15:22:46

标签: ios objective-c grand-central-dispatch

我想在一些条件检查后处理一些代码。首先是某个变量必须是true(我有一个Key-Value Observer分配给它)。第二 - 如果该变量在一段时间内没有变成true(例如5秒),那么永远不要处理变量并只处理代码。

我带来了一个明显的解决方案,我认为这是很糟糕的:每次检查while()条件和时间过去时,在另一个调度队列中循环无限true。所有这些代码都包含在另一个调度队列中......好吧,对我来说看起来不太好。

我想做的伪代码草案:

WHEN (5 seconds are gone || getCurrentDynamicExpr() == true) {
    processStuff();
} 

这样做的正确和简单方法是什么?

修改

这里看起来很混乱......要更具体:

我想在拍摄时拍摄相机,所以我想检查AVCaptureDevice的{​​{1}}属性(我正在使用isAdjustingFocus),然后拍摄一张照片。 5秒是...好吧,如果它没有聚焦,那么出现问题,所以无论如何都要拍照。

我很抱歉混乱,认为这是非常普遍的事情。

4 个答案:

答案 0 :(得分:8)

您可以考虑使用NSConditionLock lockWhenCondition:beforeDate:,或者可能安排在五秒钟内(例如,NSTimerdispatch_after)安排检查其他处理已经开始,其他处理是事件触发并设置标志。

编辑:

所以,记录:

const NSInteger kConditionLockWaiting = 0;
const NSInteger kConditionLockShouldProceed = 1;

[...]

conditionLock = [[NSConditionLock alloc] initWithCondition:kConditionLockWaiting];

[...]

dispatch_async(...
^{
    [conditionLock
         lockWhenCondition:kConditionLockShouldProceed
         beforeDate:[[NSDate date] dateByAddingTimeInterval:5.0]];

    // check the return condition to find out whether you timed out
    // or acquired the lock
});

[...]

- (void)observeValueForKeyPath:(NSString *)keyPath
        ofObject:(id)object change:(NSDictionary *)change
        context:(void *)context
{
    if(this is the appropriate property)
    {
        [conditionLock lock];
        [conditionLock unlockWithCondition:kConditionLockShouldProceed];
    }
}

答案 1 :(得分:4)

使用NSLock,调度障碍,NSOperation依赖关系以及诸如此类的东西,有很多方法可以在不同的抽象级别上完成您所做的事情。但由于您已经使用了GCD,dispatch_semaphore_*函数将完成您所需的功能。

有点像(在我的头顶,可能有拼写错误:)。

  // this is the barrier one task will use to signal others that it's done.
  dispatch_semaphore_t mySemaphore = dispatch_semaphore_create(0);
  __block NSString *result = @"not done yet";
  // adjust these to change which task "wins":
  int hardTaskLength = 3;
  int timeoutSeconds = 5;

  // this is the first task:
  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
    // do something slow and difficult
    sleep(hardTaskLength);
    result = @"now I am done";
    // then when we're done, let the world know:
    dispatch_semaphore_signal(mySemaphore);
  });

  // and this is the second, dependent one:
  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
    dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeoutSeconds * NSEC_PER_SEC));
    // wait for the first task to complete, or give up after a bit:
    BOOL timedOut = dispatch_semaphore_wait(mySemaphore, timeout);
    // now do stuff that wants the result of the first task
    if (timedOut)
      NSLog(@"Gave up waiting after %d seconds, result is: %@", timeoutSeconds, result);
    else
      NSLog(@"Processing finished; result is: %@", result);

    // these can happen wherever is appropriate, after everything is done:
    dispatch_release(mySemaphore);
    mySemaphore = 0;
  });

“Grand Central Dispatch reference”手册部分提供了有关信号量如何工作的更多信息。信号量本质上是一个线程安全的计数器; “信号”将它增加1,并且“等待”将其减少1 ......除非计数器为零;然后“等待”停止并等待,直到其他东西再次增加计数器,然后递减并继续。

来自文档:“传递零值[在dispatch semaphore_create中]对于两个线程需要协调特定事件的完成时非常有用。”这正是你正在做的事情。

[编辑添加]:

根据问题中的其他信息,调度队列看起来对你正在做的事情来说太过分了。无论如何,UIKit必须在主线程上发生,所以做这样的事情:

  • 在5秒开始时,在您关注的属性上设置KVO,并启动5秒NSTimer。
  • 在KVO听众中,拍摄照片,然后取消定时器。
  • 在计时器处理程序方法中,拍摄照片,然后取消注册KVO观察。
  • 如果首先发生异常(取消按钮?)并且您需要中止,则取消注册KVO取消定时器。

由于主线程上发生了所有事情,因此不必担心争用,并且计时器会处理阻止主线程阻塞的问题。

答案 2 :(得分:4)

由于您无法控制AVCaptureStillImageOutput的{​​{1}}(您不是将其设置为真或假),因此您无法使用我以前的答案(这就是我的意思我需要确切的情况:我们还在等什么,为什么。实现细节取决于这些信息)。

恕我直言,最好的选择确实是实现一些超时并等待就像你建议的那样。请务必使用isAdjustingFocus,这样您就不会连续投票了。

usleep()

答案 3 :(得分:3)

我会使用-[NSObject performSelector:withObject:afterDelay:]来处理超时部分。如果KVO更改通知在超时之前触发,只需使用+[NSObject cancelPreviousPerformRequestsWithTarget:selector:object:]取消超时。

不要阻止等待任何事情发生(与while循环一样)。只需返回事件循环。当任何事情发生时,它会调用你的代码。