私の唯一の理論は、停止したい実際のメソッド内で 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