保留重复的NSTimer以供以后访问?

时间:2011-02-08 17:31:58

标签: iphone objective-c cocoa-touch

我正在createTimer方法中创建一个NSTimer,我想在后面的cancelTimer方法中引用它。为方便起见,我通过保留的财产取得了NSTimer的所有权,以便我稍后再回头查看。令我困惑的问题是,如果我启动计时器,取消它并再次启动它,代码崩溃。

@property(nonatomic, retain) NSTimer *walkTimer;

-(void)createTimer {
    NSTimer *tempTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(updateTimerDisplay) userInfo:nil repeats:YES];
    [self setWalkTimer:tempTimer];
}

-(void)cancelTimer {
    [walkTimer release];
    [[self walkTimer] invalidate];
}

现在我似乎通过将cancelTimer更改为:

来解决此问题
-(void)cancelTimer {
    [self setWalkTimer:nil];
    [[self walkTimer] invalidate];
}

我很好奇为什么发布不起作用,我的理解是:

  1. NSTimer(自动发布对象,非我所有)
  2. setWalkTimer(取得我的所有权,retainCount + 1)
  3. 发布(放弃我的所有权,retainCount-1)
  4. 无效(让系统处理计时器)
  5. 修改

    // this fails ...
    -(void)cancelTimer {
        [[self walkTimer] invalidate];
        [walkTimer release];
    }
    
    // this works fine ...
    -(void)cancelTimer {
        [[self walkTimer] invalidate];
        [self setWalkTimer: nil];
    }
    

    编辑:002

    最初我认为我在混淆

    @property(nonatomic, retain) NSTimer *walkTimer;
    // &
    [self setWalkTimer];
    

    并且认为我需要一个版本来平衡属性,我不是用新集合覆盖它(要么是另一个对象或nil),最后在dealloc中释放属性。

    属性(保留)是否与保留相同,我会说不,我认为这是我出错的地方。

    编辑:003 关于这个问题,我认为我个人使用[walkTimer release]错误地将事情搞糊涂了。结果,这个话题基本上浮现了一个我写成this的新问题

3 个答案:

答案 0 :(得分:4)

在致电release之前,您invalidate。这意味着当您致电invalidate时,您已经放弃了计时器的所有权。实际上,您最终会在解除分配的计时器实例上调用invalidate

在拨打invalidate之前,您应该拨打release 。由于您使用的是保留属性,因此您只需将属性设置为nil

即可
// Schedule the timer.
self.walkTimer = [NSTimer scheduledTimerWith...];

// Cancel the timer.
[self.walkTimer invalidate];
self.walkTimer = nil;

更新以消除有关内存管理的任何混淆

重要的是要记住Objective-C的Memory Management Rules - 如果您在其上调用alloccopyretain,并且如果您拥有一个对象,你必须最终调用release。在这种情况下,setWalkTimer:会保留计时器,因为该属性声明为retain - 这意味着您拥有计时器,并且必须在路上呼叫releaseinvalidate方法计算为放弃计时器的所有权。

当您计划一个计时器时,运行循环会保留它,当计时器触发或无效时,运行循环将释放它。但实际上,您不需要知道 - 这是一个实现细节。 release之前invalidate的调用仅用于在运行循环计划定时器时平衡retain

答案 1 :(得分:0)

您需要在发布前失效。计时器启动后,您是唯一一个在计时器上保留计时器的人。因此,当您调用release时,计时器将取消分配。然后,您在无效内存上调用invalidate,然后崩溃。

答案 2 :(得分:0)

如果您将目标设置为自我,请不要保留预定的NSTimer不要将self设置为重复计时器的目标,除非您完全确定知道所有后果

(...否则运行时会在泄露的计时器,目标和userInfos中淹死一只小猫 - 或者说是这样。)

请阅读并重新阅读NSTimer Class Reference中的“概述”,并特别注意最后一段。

简而言之:

  1. 如果您安排了NSTimer,它会与保留的当前运行循环相关联。
  2. 此外,计时器retains its target
  3. NSTimer个实例无法重复使用“一旦失效,计时器对象就无法重复使用”
  4. 因此,首先保留预定的计时器是没有意义的。

    如果你需要坚持下去(例如为了取消它),请使用非拥有(又称弱)引用。

    <强>更新
    有关详细说明,请参阅my answer to your other question(它现在有图表 - 虽然只是链接 - 以及其他内容)。

    请将此帖的其余部分(以及我的许多评论)视为过时。


    您的财产变为

    @property (nonatomic, assign) NSTimer *walkTimer;
    

    顺便说一句:

    -(void)cancelTimer {
        [self setWalkTimer:nil]; // great, now [self walkTimer] returns nil so
        [[self walkTimer] invalidate]; // here, you are calling [nil invalidate]
    }
    

    由于在目标C中消息nil绝对正常,你的崩溃奇迹般地消失了...而你的计时器将很快继续发射。

    修改

    我忘了提及:
    一个计时器想要一个带有一个参数的选择器,这个参数将被触发...或者这只是一个错字?