2

条件チェック後にコードを処理したい。まず、いくつかの変数が必要であるということですtrue(Key-Value Observerが割り当てられています)。2番目-その変数がtrueしばらくの間(たとえば5秒)なっていない場合は、変数を気にせず、コードを処理するだけです。

明らかな解決策がありますが、これは悪いことだと思います。条件と経過while()時間をチェックするたびに、別のディスパッチキューで無限ループが発生します。trueそして、このコードはすべて別のディスパッチキューにラップされています...まあ、私にはよく見えません。

私がやりたいことのドラフト擬似コード:

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

これを行うための正しくて簡単な方法は何ですか?

編集

ここでは多くの混乱があるようです...より具体的になりました:

焦点が合った状態でカメラショットを撮りたいので、AVCaptureDeviceisAdjustingFocusプロパティ(使用しているAVCaptureStillImageOutput)を確認してから、写真を撮りたいと思います。5秒は..まあ、それが焦点を合わせていなかったなら、何かが間違っているので、とにかく写真を撮ってください。

混乱してすみません、それは本当に一般的なことだと思いました。

4

4 に答える 4

8

他の処理がすでに開始されているかどうかをチェックし、他の処理がイベントトリガーされてフラグが設定されているかどうかを確認するために、5秒以内に何かが発生するようにスケジュールする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;
  });

マニュアルセクション「GrandCentralDispatchreference」には、セマフォがどのように機能するかについての詳細があります。セマフォは本質的にスレッドセーフなカウンターです。カウンタがゼロでない限り、「信号」で1増加し、「待機」で1減少します。次に、「待機」が停止し、他の何かが再びカウンターを増やすまで待機してから、デクリメントして続行します。

ドキュメントから:「[dispatch semaphore_create]の値にゼロを渡すと、2つのスレッドが特定のイベントの完了を調整する必要がある場合に役立ちます。」それはまさにあなたがしていることです。

[編集して追加]:

ただし、質問の追加情報に基づくと、ディスパッチキューは、実行していることに対してやり過ぎのように見えます。とにかくすべての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 に答える