代码之家  ›  专栏  ›  技术社区  ›  Ohad Regev

当发射时间可能发生变化时,如何使用要发射的自动计划动作?

  •  0
  • Ohad Regev  · 技术社区  · 12 年前

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

    如果我说错了,如果有人能指导我如何执行这种行为,我将不胜感激。

    如果我把它写对了,但只是写错了,我也会很感激能看到我的“bug”。。。

    2 回复  |  直到 12 年前
        1
  •  1
  •   benzado    12 年前

    这不是内存管理问题

    当您计划NSTimer实例时,运行循环会保留计时器,因此无需担心自己保留计时器(无论是否为ARC)。

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

    您可以通过两种方式处理此问题:

    1. 在您的 actionMethod ,请确保检查您是否仍要执行该操作。如果没有,请忽略该消息,让一切都清理干净。

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

    如果你能确保目标总是在身边,并且总是可以收到消息,那么你什么都不需要做。例如,您的应用程序委派对象将在应用程序的整个生命周期中一直存在。

    NSTimer目标动作

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

    您需要这样定义您的方法:

    - (void)actionMethod:(NSTimer *)timer
    

    获取 某些数据输入 值,则可以将其另存为计时器的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 正在返回时间间隔,因为 现在 ,而不是自参考日期起。如果希望计时器在三秒内启动 nextFire 必须是 3.0 .如果你不小心打电话 timeIntervalSinceReferenceDate 你会得到一个巨大的数字,计时器将被安排在未来几年内启动。

        2
  •  1
  •   David H    12 年前

    我猜你使用的是ARC。你没有把定时器存储在一个强变量中,所以它在发射之前就被释放了。创建一个ivar“NSTimer*aTimer”,然后在实例化计时器时使用它而不是本地变量(我在这里不太清楚,因为我不记得runLoop是否在计划时保留了它——可能是为了完整起见,把它留在这里,因为如果由于任何原因你被“弹出”等,你应该调用计时器上的invalide)。

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

    编辑:

    将您的操作方法更改为以下格式:

    -(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,或者从计时器中获取一个值: