如何在后台线程中运行NSTimer

3
我想在后台线程中运行NSTimer,为此我编写了以下代码,但我的NSTimer方法没有被调用!请有人帮助我。
- (void)viewDidLoad {
       [super viewDidLoad];
       // Do any additional setup after loading the view.
        NSOperationQueue* operationQueue = [[NSOperationQueue alloc] init];
        [operationQueue addOperationWithBlock:^{
        // Perform long-running tasks without blocking main thread

        [NSTimer scheduledTimerWithTimeInterval:2
                                                 target:self
                                               selector:@selector(targetMethod)
                                               userInfo:nil
                                                repeats:YES];
          }];
}

-(void)targetMethod{
         NSLog(@"Timer Called");
}

[NSTimer scheduledTimerWithTimeInterval:0 target:self selector:@selector(targetMethod) userInfo:nil repeats:YES];[NSTimer scheduledTimerWithTimeInterval:0 target:self selector:@selector(targetMethod) userInfo:nil repeats:YES]; - Himanshu Moradiya
在后台线程上运行计时器似乎是个不好的主意。你实际上想要达到什么目的? - gnasher729
实际上,我并不想使用这个NsTimer来实现任何目标,我是初学者,所以想知道如何使用后台线程运行操作? - Krish
我有一个疑问,就是当我们在后台线程中运行操作并得到结果后,这个线程会自动停止吗? - Krish
4个回答

1
Krish,你似乎正在走上错误的道路。
首先,你应该通过调用scheduledTimerWithTimeInterval在主线程上创建定时器。将scheduledTimerWithTimerInterval方法放到操作队列中是毫无意义的。定时器是从其线程的运行循环中调用的。尝试使用不同的操作队列调度它们会导致麻烦。
如果您希望定时器的实际操作发生在后台,则可以在定时器回调方法中使用dispatch_async(...)来在后台运行代码。
除非您有合法的理由进行其他操作,否则请在主线程上安排定时器。
您的回调方法应该有一个计时器参数。这样,您的回调方法就可以操作计时器,例如使其失效。scheduledTimer...还返回计时器。通常,您会存储该计时器对象,以便在视图消失时可以使其失效。如果不这样做,每次viewDidLoad被调用时都会获得一个新的NSTimer。这就是说,您将拥有越来越多的定时器回调函数随着您的应用程序运行而运行。
学习GCD(Grand Central Dispatch)。它比操作队列简单得多。除非您有充分的理由,否则不应使用操作队列。
当你问“如何停止后台线程”时,你不能。你需要将代码分派到它上面,代码运行,并且只要有代码被分派,它就会一直运行。如果没有代码分派到后台,则它停止运行,直到再次分派代码。这就是它应该工作的方式。
如果你指的是“如何停止计时器” - 那就是invalidate的作用。
PS. 如果你想从后台线程中调度一个计时器(你不想这样做,相信我),可以在此处找到正确的答案:iOS5 ARC is it safe to schedule NSTimers from background selectors?

1
你可以使用GCD调度队列来进行后台线程:=
 dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void)
                   {
                       NSTimer *timer = [NSTimer timerWithTimeInterval:0.5
                                                                target:self
                                                              selector:@selector(timerFired)
                                                              userInfo:nil repeats:YES];
                       [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
                       dispatch_async(dispatch_get_main_queue(), ^(void)
                                      {
                                      });
                   });

在你的代码中,Niharika,你必须使用主线程的dispatch_get_main_queue。 - Himanshu Moradiya
是的,我们不应该使用dispatch_get_main_queue块。 - Krish
@krish,我已经编辑了我的答案,现在它可以工作了。请尝试一下。 - Niharika
我们如何停止后台线程? 在获取结果后它会自动停止吗? - Krish
你需要仅一次触发timerFired方法吗? - Niharika
timerFired方法没有被调用。 - Krish

0

你的计时器从未触发的原因是,由你的NSOperationQueue使用的后台线程可能没有NSRunloopNSTimer实例会在创建/初始化它们的线程的NSRunloop上安排自己。

顺便说一下,如果您需要停止(使无效)NSTimer,则必须在创建它的同一线程上执行此操作。

主线程提前启动并运行其NSRunloop,因为所有UI(和UI事件处理)都在该runloop上完成。这就是为什么在主线程上使用NSTimer最容易的原因。

一个可能的解决方案是为您要使用的后台线程创建一个NSRunloop,例如通过调用:

[NSRunloop currentRunLoop];

在添加到NSOperationQueue中的块操作中。文档指出,如果当前线程没有运行循环,则currentRunLoop将在调用时创建一个并返回它。

现在另一个可能的问题是,NSOperationQueue(特别是您创建和初始化的方式)使用不确定数量的线程,可以在途中创建和处理线程,并且以任何方式都不承诺它们的生命周期。所以...我的解决方案可能会失效。

与其使用NSOperationQueue,您应该创建一个后台线程,创建并启动其NSRunloop,然后在该runloop上安排计时器。

这里已经提到了另一个可能的解决方案-使用dispatch_source计时器而不是NSTimer,而且还有比这更好的答案。


0
一个 NSTimer 实际上并没有在线程上运行。将计时器调度到主线程(实际上是它的 NSRunLoop)仍然允许它在计时器触发之前处理事件和执行其他操作。
当计时器触发时,主线程的 NSRunLoop 将调用目标+选择器,然后继续等待下一个事件。
我建议您使用以下代码替换您的代码:
- (void)viewDidLoad {
       [super viewDidLoad];
       // Do any additional setup after loading the view.
       [NSTimer scheduledTimerWithTimeInterval:2
                                        target:self
                                      selector:@selector(targetMethod)
                                      userInfo:nil
                                       repeats:YES];
}

-(void)targetMethod{
         NSLog(@"Timer Called");
}

注意:如果targetMethod需要执行昂贵的操作,建议将它们安排到后台线程中运行。

没问题,但这不是 OP 所要求的... 如果你不能在后台线程上运行它,就直说吧... - Motti Shneor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接