我想在一些条件检查后处理一些代码。首先是某个变量必须是true
(我有一个Key-Value Observer分配给它)。第二 - 如果该变量在一段时间内没有变成true
(例如5秒),那么永远不要处理变量并只处理代码。
我带来了一个明显的解决方案,我认为这是很糟糕的:每次检查while()
条件和时间过去时,在另一个调度队列中循环无限true
。所有这些代码都包含在另一个调度队列中......好吧,对我来说看起来不太好。
我想做的伪代码草案:
WHEN (5 seconds are gone || getCurrentDynamicExpr() == true) {
processStuff();
}
这样做的正确和简单方法是什么?
修改
这里看起来很混乱......要更具体:
我想在拍摄时拍摄相机,所以我想检查AVCaptureDevice
的{{1}}属性(我正在使用isAdjustingFocus
),然后拍摄一张照片。 5秒是...好吧,如果它没有聚焦,那么出现问题,所以无论如何都要拍照。
我很抱歉混乱,认为这是非常普遍的事情。
答案 0 :(得分:8)
您可以考虑使用NSConditionLock
lockWhenCondition:beforeDate:
,或者可能安排在五秒钟内(例如,NSTimer
或dispatch_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必须在主线程上发生,所以做这样的事情:
由于主线程上发生了所有事情,因此不必担心争用,并且计时器会处理阻止主线程阻塞的问题。
答案 2 :(得分:4)
由于您无法控制AVCaptureStillImageOutput
的{{1}}(您不是将其设置为真或假),因此您无法使用我以前的答案(这就是我的意思我需要确切的情况:我们还在等什么,为什么。实现细节取决于这些信息)。
恕我直言,最好的选择确实是实现一些超时并等待就像你建议的那样。请务必使用isAdjustingFocus
,这样您就不会连续投票了。
usleep()
答案 3 :(得分:3)
我会使用-[NSObject performSelector:withObject:afterDelay:]
来处理超时部分。如果KVO更改通知在超时之前触发,只需使用+[NSObject cancelPreviousPerformRequestsWithTarget:selector:object:]
取消超时。
不要阻止等待任何事情发生(与while
循环一样)。只需返回事件循环。当任何事情发生时,它会调用你的代码。