7

ユーザー入力を介してアニメーション化するヘルスバーを実装しています。

これらのアニメーションは、特定の量 (たとえば 50 単位) だけ上下に移動し、ボタンを押した結果です。2 つのボタンがあります。増減。

一度に 1 つのスレッドのみが変更できるように、ヘルス バーのロックを実行したいと考えています。問題は、デッドロックが発生していることです。

別のスレッドが別のスレッドによって保持されているロックを実行するためだと思います。しかし、アニメーションが完了すると、そのロックは解除されます。[UIView AnimateWithDuration] が完了したときに終了するロックをどのように実装しますか?

進むべき道なのだろうかNSConditionLockと思いますが、不必要な複雑さを避けるために、可能であれば NSLocks を使用したいと考えています。おすすめは何ですか?

(最終的には、ユーザー入力を継続しながらアニメーションを「キューに入れる」必要がありますが、今のところ、最初に入力をブロックしても、ロックを機能させたいだけです。)

(うーん、同じ UIView に対して一度に実行される [UIView AnimateWithDuration] は 1 つしかないことを考えてみてください。2 番目の呼び出しは最初の呼び出しを中断し、最初の完了ハンドラーをすぐに実行させます。おそらく、2 番目のロックは、最初にロックを解除する機会があります. この場合、ロックを処理する最良の方法は何ですか? おそらく、グランドセントラルディスパッチに再度アクセスする必要がありますが、より簡単な方法があるかどうかを確認したかった.)

ViewController.h で次のように宣言します。

NSLock *_lock;

ViewController.m には次のものがあります。

ロードビューで:

_lock = [[NSLock alloc] init];

ViewController.m の残りの部分 (関連部分):

-(void)tryTheLockWithStr:(NSString *)str
{
    LLog(@"\n");
    LLog(@" tryTheLock %@..", str);

    if ([_lock tryLock] == NO)
    {
         NSLog(@"LOCKED.");
    }
          else
    {
          NSLog(@"free.");
          [_lock unlock];
    }
}

// TOUCH DECREASE BUTTON
-(void)touchThreadButton1
{
    LLog(@" touchThreadButton1..");

    [self tryTheLockWithStr:@"beforeLock"];
    [_lock lock];
    [self tryTheLockWithStr:@"afterLock"];

    int changeAmtInt = ((-1) * FILLBAR_CHANGE_AMT); 
    [self updateFillBar1Value:changeAmtInt];

    [UIView animateWithDuration:1.0
                      delay:0.0
                    options:(UIViewAnimationOptionTransitionNone|UIViewAnimationOptionBeginFromCurrentState|UIViewAnimationOptionAllowUserInteraction)
                 animations:
     ^{

         LLog(@" BEGIN animationBlock - val: %d", self.fillBar1Value)
         self.fillBar1.frame = CGRectMake(FILLBAR_1_X_ORIGIN,FILLBAR_1_Y_ORIGIN, self.fillBar1Value,30);
     }
     completion:^(BOOL finished)
     {
         LLog(@" END animationBlock - val: %d - finished: %@", self.fillBar1Value, (finished ? @"YES" : @"NO"));

         [self tryTheLockWithStr:@"beforeUnlock"];
         [_lock unlock];
         [self tryTheLockWithStr:@"afterUnlock"];         
     }
     ];
}

-(void)updateFillBar1Value:(int)changeAmt
{
    self.prevFillBar1Value = self.fillBar1Value;

    self.fillBar1Value += changeAmt;

    if (self.fillBar1Value < FILLBAR_MIN_VALUE)
    {
        self.fillBar1Value = FILLBAR_MIN_VALUE;
    }
    else if (self.fillBar1Value > FILLBAR_MAX_VALUE)
    {
        self.fillBar1Value = FILLBAR_MAX_VALUE;
    }
}

出力:

指示を再現するには:「減らす」を1回タップします

touchThreadButton1..

tryTheLock beforeLock..無料。

tryTheLock afterLock.. ロックされています。BEGIN animationBlock - 値: 250 END animationBlock - 値: 250 - 終了: はい

ロックを解除する前にロックを試してください..ロックされています。

tryTheLock afterUnlock..無料。

結論:これは期待どおりに機能します。

--

出力:

手順を再現するには: [減少] をすばやく 2 回タップします (最初のアニメーションを中断します)。

touchThreadButton1..

tryTheLock beforeLock..無料。

tryTheLock afterLock.. ロックされています。BEGIN animationBlock - 値: 250 touchThreadButton1..

tryTheLock beforeLock.. LOCKED. * -[NSLock lock]: デッドロック ( '(null)') * _NSLockError() でブレークしてデバッグします。

結論。デッドロック エラー。ユーザー入力は凍結されています。

4

1 に答える 1