【问题标题】:iOS5 ARC is it safe to schedule NSTimers from background selectors?iOS5 ARC 从后台选择器调度 NSTimers 是否安全?
【发布时间】:2011-11-09 05:09:09
【问题描述】:

我正在尝试调试我的应用程序。

我一直在我的非弧形代码中使用一些 NSTimer 实例(来自主线程):

[NSTimer scheduledTimerWithTimeInterval:5 target:musicPlayer selector:@selector(playPause:) userInfo:nil repeats:NO];

如果我将此代码分配给一个按钮并单击一个按钮,这会很好。计时器触发。

我也试过了:

 if( self.deliveryTimer == nil)
  {                 
self.deliveryTimer = [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(playPause:) userInfo:nil repeats:NO];
    }

    -(void)playPause:(NSTimer*)timer
    {
           [deliveryTimer invalidate];
            deliveryTimer = nil;
//more code here

    }

我希望计时器能够执行,点击下面的播放/暂停方法,然后变为 nil,这样我可以稍后重置计时器。我检查 nil 的原因是因为我有 3 个不同的代码路径可以设置计时器。每个都有一个 NSLog 语句,表明定时器已经被调度。

我的代码运行,我看到计时器正在安排,但它们似乎没有在正常的应用程序执行过程中触发。我正在调查原因。短期计时器,使用相同的逻辑触发罚款。当我让应用程序运行一段时间时,我遇到了问题。

NSTimers 能被 ARC 回收吗?

如果我从 performSelectorInBackground 设置计时器是否重要?当我写这个问题时,我注意到我的一些计时器是从一个被调用的代码路径创建的:

[self performSelectorInBackground:@selector(notifyDelegateOfDataPoint:) withObject:data];

背景选择器可能是我的计时器没有更早触发/回收的原因吗?

感谢您的帮助,这个错误已经困扰我超过 2 周了!

更新:将代码更改为使用 NSTimers 的主线程后,计时器正确触发,导致音乐播放:

   [self performSelectorOnMainThread:@selector(deliverReminder:) withObject:nil waitUntilDone:NO];


-(void)deliverReminder:(id)sender{
     [ NSTimer scheduledTimerWithTimeInterval:10 target:reminderDeliverySystem selector:@selector(playAfterDelay:) userInfo:nil repeats:NO];
    [self postMessageWithTitle:nil message:@"Deliver Reminder Called" action:kNoContextAction];
}

-(void)playAfterDelay:(id)sender
{
    int reminderDelay = reminder.delayValue.intValue;

    [playTimers addObject:[NSTimer scheduledTimerWithTimeInterval:reminderDelay target:self selector:@selector(appMusicPlayerPlay:) userInfo:nil repeats:NO]];


}

这里我有一大堆计时器,这是因为我不知道如何使用选择器将原语传递给目标。

【问题讨论】:

  • all 计时器显然未能触发,还是只是一些?他们的火法还有什么?如果计时器被安排在后台线程上,它也会在该线程上触发,这可能会导致奇怪的事情发生。
  • 所有通过 performSelectorInBackground 调度的定时器行为不一致。我听过他们玩过几次,但大多数时候,他们只是失败了。我什至没有收到 NSLog 消息或控制台更新。你对奇怪的事情是正确的,这准确地描述了我所经历的。

标签: objective-c ios ios5 nstimer automatic-ref-counting


【解决方案1】:

NSTimer 需要在该后台线程中运行一个运行循环才能继续触发。主线程已经有一个活动的运行循环,这就是为什么你的计时器在它上面执行时可以正常工作的原因。

如果您想在后台线程中使用计时器,您可以执行以下操作:

NSRunLoop* runLoop = [NSRunLoop currentRunLoop];
self.deliveryTimer = [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(playPause:) userInfo:nil repeats:NO];
[runLoop run];

您的短时间计时器触发可能发生的情况,但较长时间的计时器不会发生,是它们在线程仍处于活动状态时触发,但没有运行循环来保持它继续运行,在线程到达结束后失败执行。

我不认为这是与 ARC 相关的,尽管您可能需要注意一些事情,因为 NSRunLoop 会保留一个附加到它的计时器。遵循 NSTimers 的标准程序应该可以避免 ARC 问题。

【讨论】:

  • 感谢您的澄清,现在我明白发生了什么。就后台线程而言,是只有 1 个,还是每次调用都创建一个新线程?我每隔几秒调用一次 performSelectorInBackground,如果我理解正确,我只会在安排计时器时为几个实例创建运行循环。这些线程将继续在运行循环中运行,直到计时器触发。然后线程将终止并回收它的资源。是这样吗?
  • @AlexStone - 每个调用都应该创建一个新线程。您可能需要手动终止线程的运行循环以终止它。老实说,如果您只想在某个延迟后在后台运行任务,我会使用全局并发队列之一查看 GCD 的 dispatch_after()。它会更简单,并且会避免所有这些线程的开销。
  • 关于[runLoop run] 有两点值得注意:1) 在运行循环终止之前,控制不会越过那行代码 2) 你不能终止通过NSRunLoop 接口运行循环——您需要获取底层CFRunLoop 并使用CFRunLoopStop()。使用 GCD 的更多理由!
猜你喜欢
  • 1970-01-01
  • 2012-02-15
  • 2011-08-19
  • 1970-01-01
  • 1970-01-01
  • 2012-11-11
  • 2023-03-13
  • 2010-12-07
  • 2013-12-23
相关资源
最近更新 更多