如何在发射时间时触发自动预定动作可能会改变?

时间:2012-08-12 13:21:27

标签: ios nstimer nstimeinterval

在某个视图中,我希望在触发时间基于NSTimeIntervals数组(我保留为NSArray保持双值)时触发某个方法。

所以我有一个根据相关时间执行相关操作的方法,并且在此方法中,我调用相同的方法(递归地)在下一个预定时间再次触发:

-(void) actionMethod:(int)someDataINeed {

    //do something and then...

    if (someDataINeed == someDefinitionIHaveToBreakTheLoop) {
        return;
    }

    double nextFire = [self aMethodThatCalculatesTheNextFiringTime];
    NSNumber * nextDataINeed = [NSNumber numberWithInt:someDataINeed+1];

    NSTimer * aTimer = [NSTimer scheduledTimerWithTimeInterval:nextFire 
                                        target:self 
                                        selector:@selector(actionMethod:)
                                        userInfo:nextDataINeed 
                                        repeats:NO];
}

嗯......它不起作用(如果确实如此,我想我不会问它......)。 当我对它进行NSLog时,似乎时间根本没有运行,并且在某种循环中调用定时器而不是根据我定义的时间“被触发”。 nextFire双数据是正确的(根据我的定义)。

如果我错了,如果有人能指导我应该如何执行这类行动,我将不胜感激。

如果我做对了,但只是写错了,那么抓住我的“虫子”的眼睛也会受到赞赏......

2 个答案:

答案 0 :(得分:1)

我猜你正在使用ARC。您不会将计时器存储在强变量中,因此它会在触发之前释放。创建一个ivar" NSTimer * aTimer",然后在实例化计时器时使用它而不是局部变量(我在这里胡言乱语,因为我不记得runLoop是否在其计划时保留它 - 可能但是将此保留为完整性,因为如果出于任何原因,您应该在计时器上调用invalidate,并弹出"等等。

此外,您的计时器还会发送消息:actionMethod:(NSSTimer *)本身,但您的操作方法需要一个int。你需要调和它。

编辑:

将actionMethod更改为以下格式:

-(void)actionMethod:(id)something
{
  int foo = 0;
  if([something isKindOfClass:[NSNumber class]]) {
    foo = [foo intValue];
  } else
  if([something isKindOfClass:[NSTimer class]]) {
    foo = ... // however you get your value
  } else
     assert(!"Yikes! Wrong class");
  }
  ...

然后你可以发送一个NSNumber或让计时器用计时器调用它。您可以从NSNumber中提取int,或从计时器中获取值:

答案 1 :(得分:1)

这不是内存管理问题

当您安排NSTimer实例时,运行循环会保留计时器,因此无需担心自己保留(ARC或不是)。

计时器还会保留其目标,因此您不必担心首先释放目标的情况。但是,如果目标是视图,则在从视图层次结构中删除后,它可能会收到计时器消息。在这种情况下,您将要忽略该消息。

您可以通过以下两种方式处理:

  1. actionMethod中,请务必检查您是否仍要执行此操作。如果没有,请忽略该消息并清除所有内容。

  2. 保留对计时器实例的引用,并在不再希望接收计时器消息时调用[timer invalidate]

  3. 如果您可以确定目标始终存在并且始终可以接收消息,则无需执行任何操作。例如,您的应用委托对象将在您的应用程序的生命周期内使用。

    NSTimer的目标行动

    您无法设置计时器来调用任何对象上的任何方法。计时器方法必须只接受一个参数,即发送消息的NSTimer实例。您已定义actionMethod:以获取int,它将实际编译并执行,但是当计时器触发并调用该方法时,someDataINeed的值将是- (void)actionMethod:(NSTimer *)timer 的内存地址。 NSTimer对象。这不是很有用。

    您需要以这种方式定义方法:

    someDataINeed

    要获取 NSNumber *number = [timer userInfo]; int someDataINeed = [number intValue]; 值,可以将其保存为计时器userInfo中的NSNumber。它只是为了你可以添加一些数据到计时器。

    nextDataINeed

    创建计时器时,可以将 currentTimer = [NSTimer scheduledTimerWithTimeInterval:nextFire target:self selector:@selector(actionMethod:) userInfo:[NSNumber numberWithInt:nextDataINeed] repeats:NO]; 包装到NSNumber中并以这种方式设置userInfo:

    aMethodThatCalculatesTheNextFiringTime

    您需要使用NSNumber,因为userInfo必须是对象。

    可能的错误

    如果应用上述方法无法解决问题,那么要检查的一件事是nextFire返回自 now 以来的时间间隔,而不是自参考日期以来。如果您希望计时器在三秒内触发,则3.0的值必须为timeIntervalSinceReferenceDate。如果你不小心打电话给{{1}},你会得到一个巨大的数字,而计时器将安排在未来几年开火。