【问题标题】:How to use automatic scheduled actions to be fired when firing timing may change?当触发时间可能发生变化时,如何使用要触发的自动计划动作?
【发布时间】:2012-08-12 13:21:27
【问题描述】:

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

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

如果我做对了,但只是写错了,我也很感激能抓住我的“错误”……

【问题讨论】:

    标签: ios nstimer nstimeinterval


    【解决方案1】:

    不是内存管理问题

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

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

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

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

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

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

    NSTimer 的目标动作

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

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

    - (void)actionMethod:(NSTimer *)timer
    

    要获取someDataINeed 值,您可以将其保存为计时器的用户信息中的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,您会得到一个巨大的号码,并且计时器将被安排在几年后触发。

    【讨论】:

    • 感谢 benzado - 我阅读了几个教程,解释了如何使用这些获取 (NSTimer*) 作为参数的方法 - 你的回答是我第一次真正得到它。 U 规则!
    【解决方案2】:

    我猜你正在使用 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,或者从计时器中获取值:

    【讨论】:

    • 大卫,感谢您的回答。几个澄清 - 我确实为 NSTimer 使用了 ivar,我稍微简化了代码(因为在应用程序中有几个元素到位)。我阅读了 Apple 的文档,并没有真正理解“NSTimer 自身失效”的概念。另外,您能否进一步说明我应该在我的方法中协调什么? “int”值用于捕获 NSArray 中的相关对象,该方法不需要 NSTimer - 它需要自动触发并且需要为下一个触发事件定义一个新的 NSTimer。
    • 因此,当计时器向您发送消息时, someDataINeed 是指向刚刚触发的 NSTimer 的指针。计时器保留“自我”,因此如果计时器处于活动状态,您将无法解除分配。每当你想准备释放当前对象时,你必须向计时器发送“invalidate”,它告诉它停止并将其目标设置为 nil,然后释放它。
    • 好的,我遇到了无效问题。关于 NSTimer>userInfo 参数的工作方式>>那么,我应该在我的 actionMethod 中更改什么?我在一些文档中看到我应该写一些类似“-(void) actionMethod:(NSTimer*)aTimer”的东西。这就是你说“你需要调和那个”时的意思(如果是这样,我该如何收集“int someDataINeed”值?)还是应该将“Target”参数从“self”更改为其他值(如果那么,对什么?)?如果您能澄清这一点 - 我将非常感激。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-21
    • 2017-03-17
    • 1970-01-01
    • 1970-01-01
    • 2016-08-10
    • 2012-03-11
    相关资源
    最近更新 更多