0

在某个视图中,我希望在触发时间基于 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 双数据是正确的(根据我的定义)。

如果我错了,如果有人能指导我如何执行此类操作,我将不胜感激。

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

4

2 回答 2

1

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

您的计时器也发送消息: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,或者从计时器中获取值:

于 2012-08-12T17:13:54.650 回答
1

这不是内存管理问题

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

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

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

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

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

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

NSTimer 的目标动作

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

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

- (void)actionMethod:(NSTimer *)timer

要获取该someDataINeed值,您可以将其保存为计时器的 userInfo 中的 NSNumber。它的存在只是为了让您可以向计时器添加一些数据。

    NSNumber *number = [timer userInfo];
    int someDataINeed = [number intValue];

创建计时器时,您可以将其包装nextDataINeed成 NSNumber 并以这种方式设置 userInfo:

    currentTimer = [NSTimer scheduledTimerWithTimeInterval:nextFire 
                            target:self 
                            selector:@selector(actionMethod:)
                            userInfo:[NSNumber numberWithInt:nextDataINeed]
                            repeats:NO];

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

可能的错误

如果应用上述方法不能解决问题,要检查的一件事aMethodThatCalculatesTheNextFiringTime是返回自now以来的时间间隔,而不是自参考日期以来的时间间隔。如果您希望计时器在三秒内触发,则 的值nextFire必须为3.0。如果你不小心打电话给timeIntervalSinceReferenceDate你,你会得到一个巨大的号码,并且计时器将被安排在几年后触发。

于 2012-08-13T16:33:24.977 回答