【问题标题】:quitting a void method on a timer退出计时器上的 void 方法
【发布时间】:2013-03-24 11:04:08
【问题描述】:

我有一个与录制视频同时运行的方法。当方法结束时,它会触发一系列其他方法,这些方法会一直持续到录制结束。我希望能够按下一个按钮来提前停止录制,同时也退出该方法。我目前尝试使用的方法是使用 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 而是在不同的方法中调用,但即使是这种情况,我也不确定如何解决这个问题,因为就我而言知道计时器只能指向其他方法,我不能只告诉它我想要它在我想要停止的方法内部做什么。如果这不是问题,那么我真的不确定为什么这不起作用。

【问题讨论】:

  • 如果您希望计时器停止触发,您需要使其无效。 [self.stopTimer 无效]

标签: ios objective-c cocoa-touch nstimer


【解决方案1】:

如果计时器有效,您可以将其无效(即停止计时器)。

我不确定所有检查是否真的有必要(以及最后一行),但我目前是这样做的:

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

已编辑:

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

【讨论】:

  • 检查!= nil确实没必要; [nil isValid] 计算结果为 NO
  • 感谢您的澄清!
【解决方案2】:

我唯一的理论是,这是因为我没有在要停止的实际方法中调用 return 而是在不同的方法中调用

你的理论是正确的。 return 结束它所在的函数或方法,仅此而已。它将当前函数的上下文从堆栈中弹出,并将执行返回给调用函数。

我不确定如何解决这个问题,因为据我所知,计时器只能指向其他方法,我不能只告诉它我想要它在我想要停止的方法中做什么

我们可以使用对象来存储状态并使用该状态来控制程序的流程。该状态可以不断更新和检查。对于需要取消以响应状态变化的长时间运行的任务,状态必须与任务并行更新。既然您说计时器用于停止音频,但在 method 中完成的工作没有,我假设 method 已经在异步执行其长时间运行的任务。

这需要在后台执行一个异步长时间运行的任务(或一系列任务),并有可能取消,这与NSOperationNSOperationQueue 类非常匹配。

您可以通过实现方法或块在 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

【讨论】:

  • 伙计,这比我想象的要复杂得多,但是谢谢!我会仔细看看你发布的内容,看看我是否能弄清楚如何让它满足我的需要;非常感谢您的帮助!
  • @lunadiviner 我希望我的例子没有过于复杂。这实际上并不是非常困难,但我需要将一些计时器和块放在一起来尝试模拟您的问题,希望这些不会过于模糊重点。我忘记推荐的一件事是查看 Apple 的 concurrency programming guide。如果您有任何问题或者我可以澄清我的答案,请告诉我。
猜你喜欢
  • 2014-02-07
  • 1970-01-01
  • 2020-03-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-01
  • 1970-01-01
相关资源
最近更新 更多