1

ビデオの録画と同時に実行するメソッドがあります。メソッドが終了すると、記録が終了するまで続く一連の他のメソッドが開始されます。ボタンを押して録音を途中で停止し、同時にメソッドを終了できるようにしたい。私が現在試みている方法は、録音がまだ行われているかどうかを確認する NSTimer を使用することです。そうでない場合は、オーディオの再生を停止し、return を呼び出してメソッドを停止する必要があります。

-(void) method
{
    self.stopTimer = [NSTimer scheduledTimerWithTimeInterval:.05 target:self selector:@selector(checkRecording) userInfo:nil repeats:YES];

    // Stuff happens
}

-(void) checkRecording
{
if (isRecording == NO)
    {
        if (player.playing == YES)
        {
            [player stop];
        }
        return;
    }

}

これにより音声がすぐに停止しますが、メソッドは完了するまで実行を続けます。シーケンスの次のメソッドは呼び出されません。これは正しい方向への一歩ですが、すぐに停止する必要があります。私の唯一の理論は、実際のメソッド内で return を呼び出していないため、代わりに別のメソッドで停止したいということですが、その場合でも、それを修正する方法がよくわかりません。タイマーは他のメソッドのみを指すことができ、停止したいメソッド内で何をしたいのかを伝えることはできません。それが問題ではない場合、なぜこれが機能しないのか本当にわかりません。

4

2 に答える 2

0

私の唯一の理論は、停止したい実際のメソッド内で return を呼び出していないためであり、代わりに別のメソッドで呼び出しているということです

あなたの理論は正しいです。returnそれが入っている関数またはメソッドを終了し、それ以外は終了しません。現在の関数のコンテキストをスタックからポップし、呼び出し元の関数に実行を返します。

私の知る限り、タイマーは他のメソッドのみを指すことができ、停止したいメソッド内で何をしたいのかを伝えることができないため、それを修正する方法がよくわかりません

オブジェクトを使用して状態を保存し、その状態を使用してプログラムの流れを制御できます。その状態は、継続的に更新および確認できます。その状態の変化に応じてキャンセルする必要がある長時間実行タスクでは、タスクと並行して状態を更新する必要があります。タイマーはオーディオを停止するために機能するが、作業はmethod機能しないと言うので、method長時間実行されるタスクをすでに非同期で実行していると思います。

バックグラウンドで非同期の長時間実行タスク (または一連のタスク) を実行する必要があり、キャンセルの可能性があることは、NSOperationおよびNSOperationQueueクラスとうまく一致しています。

NSOperationメソッドまたはブロックの実装を介して、オブジェクト内で作業を実行できます。コードを実装して、適切なタイミングで操作がキャンセルされたかどうかを確認し、キャンセルされたらすぐに救済します。

以下は、うまくいけばユースケースに一致する例です。iOS アプリの「空のアプリケーション」テンプレートで作成され、すべてがアプリケーション デリゲートにあります。アプリのデリゲートは、キャンセルするかどうかを決定するために必要な状態を追跡し、その状態の変化をポーリングするタイマーをスケジュールします。キャンセルする必要があると判断した場合は、作業の実際のキャンセルを操作キューとその操作に委任します。

#import "AppDelegate.h"


@interface AppDelegate ()

@property (nonatomic) BOOL shouldStop; // Analogous to your isRecording variable
@property (nonatomic, strong) NSOperationQueue *operationQueue; // This manages execution of the work we encapsulate into NSOperation objects

@end

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Typical app delegate stuff
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];

    // Start our long running method - analogous to method in your example
    [self method];

    return YES;
}


- (void)method
{
    // allocate operation queue and set its concurrent operation count to 1. this gives us basic ordering of
    // NSOperations. More complex ordering can be done by specifying dependencies on operations.
    self.operationQueue = [[NSOperationQueue alloc] init];
    self.operationQueue.maxConcurrentOperationCount = 1;

    //  We create three NSBlockOperations. They only sleep the thread a little while,
    //  check if they've been cancelled and should stop, and keep doing that for a few seconds.
    //  When they are completed (either through finishing normally or through being cancelled, they
    //  log a message
    NSMutableArray *operations = [NSMutableArray array];
    for (int i = 0; i < 3; i++) {

        //  Block operations allow you to specify their work by providing a block.
        //  You can override NSOperation to provide your own custom implementation
        //  of main, or start, depending. Read the documentation for more details.
        //  The principle will be the same - check whether one should cancel at each
        //  appropriate moment and bail out if so

        NSBlockOperation *operation = [[NSBlockOperation alloc] init];

        //  For the "weak/strong dance" to avoid retain cycles
        __weak NSBlockOperation *weakOperation = operation;

        [operation addExecutionBlock:^{
            //  Weak/strong dance
            NSBlockOperation *strongOperation = weakOperation;

            //  Here is where you'd be doing actual work
            //  Either in a block or in the main / start
            //  method of your own NSOperation subclass.
            //  Instead we sleep for some time, check if
            //  cancelled, bail out if so, and then sleep some more.
            for (int i = 0; i < 300; i++) {
                if ([strongOperation isCancelled]) {
                    return;
                }
                usleep(10000);
            }
        }];

        //  The completion block is called whether the operation is cancelled or not.
        operation.completionBlock = ^{
            //  weak/strong dance again
            NSBlockOperation *strongOperation = weakOperation;
            NSLog(@"Operation completed, %@ cancelled.", [strongOperation isCancelled] ? @"WAS" : @"WAS NOT");
        };

        [operations addObject:operation];
    }

    //  Set up a timer that checks the status of whether we should stop.
    //  This timer will cancel the operations if it determines it should.
    [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(checkShouldKeepGoing:) userInfo:nil repeats:YES];

    //  Use GCD to simulate a stopped recording to observe how the operations react to that.
    //  Comment out to see the usual case.
    double delayInSeconds = 5;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        self.shouldStop = YES;
    });

    //  Add the operations to the operation queue, exeuction will start asynchronously from here.
    [self.operationQueue addOperations:operations waitUntilFinished:NO];
}


//  If we should stop, cancel the operations in the queue.
- (void)checkShouldKeepGoing:(NSTimer *)timer
{
    if (self.shouldStop) {
        NSLog(@"SHOULD STOP");
        [timer invalidate];
        [self.operationQueue cancelAllOperations];
    }
}

@end
于 2013-04-03T01:15:11.623 に答える
0

タイマーが有効な場合は、それを無効にすることができます (タイマーを停止します)。

すべてのチェックが本当に必要かどうか (および最後の行) はわかりませんが、現在はそのようにしています:

if ( myTimer != nil && [myTimer isValid] )
{
    [myTimer invalidate];
    myTimer = nil;
}

編集:

if ( [myTimer isValid] )
{
    [myTimer invalidate];
    myTimer = nil;
}
于 2013-04-03T00:18:31.040 に答える