2

我想在进行一些条件检查后处理一些代码。首先是一些变量必须是true(我有一个键值观察器分配给它)。其次 - 如果该变量已经有true一段时间没有变为(例如 5 秒),那么就不要理会该变量,只需处理代码即可。

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

我想做的伪代码草案:

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

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

编辑

这里似乎有很多混乱......必须更具体:

我想在对焦时捕捉相机镜头,所以我想检查AVCaptureDevice' 的isAdjustingFocus属性(我正在使用AVCaptureStillImageOutput),然后捕捉镜头。5 秒是为了......好吧,如果它没有聚焦,那就是有问题,所以无论如何都要拍照。

我很抱歉造成混乱,认为这很常见..

4

4 回答 4

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];
    }
}
于 2013-03-23T15:27:28.473 回答
5

由于您无法控制AVCaptureStillImageOutput's isAdjustingFocus(您不是将其设置为 true 或 false 的人),因此您不能使用我以前的答案(这就是我的意思是我需要确切的情况:我们在等什么,以及为什么。实现细节取决于这些信息)。

恕我直言,最好的选择确实是按照您的建议实施一些超时并等待它。一定要使用usleep(),这样你就不会连续轮询。

NSDate* date = [NSDate date];
while (TRUE)
{

   if (myBOOL)
   {
       // the condition is reached
       break;
   }

   if ([date timeIntervalSinceNow] < -5)
   {
    // the condition is not reached before timeout
    break;
   }

   // adapt this value in microseconds.
   usleep(10000);
}
于 2013-03-23T16:59:50.453 回答
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 参考”包含有关信号量如何工作的更多信息。信号量本质上是一个线程安全的计数器;“发信号”将其加一,“等待”将其减一……除非计数器为零;然后“等待”停止并等待,直到其他东西再次增加计数器,然后递减并继续。

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

[编辑添加]:

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

  • 在 5 秒开始的时候,在你关心的属性上设置 KVO,并启动一个 5 秒的 NSTimer。
  • 在KVO监听器中,拍照,取消定时器。
  • 在计时器处理程序方法中,拍照,并注销 KVO 观察。
  • 如果首先发生异常情况(取消按钮?)并且您需要中止,则两者都取消注册 KVO 取消计时器。

由于一切都发生在主线程上,因此不必担心争用,并且计时器会处理防止主线程阻塞的情况。

于 2013-03-23T16:16:19.877 回答
3

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

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

于 2013-03-23T16:34:54.000 回答