NSTimer

看到这个标题,你可能会想NSTimer不就是计时器吗,谁不会用,不就是一个能够定时的完成任务的东西吗?

  我想说你知道NSTimer会retain你添加调用方法的对象吗?你知道NSTimer是要加到runloop中才会起作用吗?你知道NSTimer会并不是准确的按照你指定的时间触发的吗?你知道NSTimer就算添加到runloop了也不一定会按照你想象中的那样执行吗?

  如果上面提出的哪些问题,你并不全部了解,那么请细心的看完下面的文章,上面的那几个问题我会一一说明,并给出详细的例子。

一、什么是NSTimer

  官方给出解释是“A timer provides a way to perform a delayed action or a periodic action. The timer waits until a certain time interval has elapsed and then fires, sending a specified message to a specified object. ” 翻译过来就是timer就是一个能在从现在开始的后面的某一个时刻或者周期性的执行我们指定的方法的对象。

 

二、NSTimer和它调用的函数对象间到底发生了什么

   从前面官方给出的解释可以看出timer会在未来的某个时刻执行一次或者多次我们指定的方法,这也就牵扯出一个问题,如何保证timer在未来的某个时刻触发指定事件的时候,我们指定的方法是有效的呢?

  解决方法很简单,只要将指定给timer的方法的接收者retain一份就搞定了,实际上系统也是这样做的。不管是重复性的timer还是一次性的timer都会对它的方法的接收者进行retain,这两种timer的区别在于“一次性的timer在完成调用以后会自动将自己invalidate,而重复的timer则将永生,直到你显示的invalidate它为止”。

  下面我们看个小例子:

 

SvTestObject.m
复制代码
//
//  SvTestObject.m
//  SvTimerSample
//
//  Created by  maple on 12/19/12.
//  Copyright (c) 2012 maple. All rights reserved.
//

#import "SvTestObject.h"

@implementation SvTestObject

- (id)init
{
    self = [super init];
    if (self) {
        NSLog(@"instance %@ has been created!", self);
    }
    
    return self;
}

- (void)dealloc
{
    NSLog(@"instance %@ has been dealloced!", self);
    
    [super dealloc];
}

- (void)timerAction:(NSTimer*)timer
{
    NSLog(@"Hi, Timer Action for instance %@", self);
}

@end
复制代码
SvTestObject.h
复制代码
//
//  SvTestObject.h
//  SvTimerSample
//
//  Created by  maple on 12/19/12.
//  Copyright (c) 2012 maple. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface SvTestObject : NSObject

/*
 * @brief timer响应函数,只是用来做测试
 */
- (void)timerAction:(NSTimer*)timer;

@end
复制代码
SvTimerAppDelegate.m
复制代码
- (void)applicationDidBecomeActive:(UIApplication *)application
{
    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
    
    // test Timer retain target
    [self testNonRepeatTimer];
//    [self testRepeatTimer];
}

- (void)testNonRepeatTimer
{
    NSLog(@"Test retatin target for non-repeat timer!");
    SvTestObject *testObject = [[SvTestObject alloc] init];
    [NSTimer scheduledTimerWithTimeInterval:5 target:testObject selector:@selector(timerAction:) userInfo:nil repeats:NO];
    [testObject release];
    NSLog(@"Invoke release to testObject!");
}

- (void)testRepeatTimer
{
    NSLog(@"Test retain target for repeat Timer");
    SvTestObject *testObject2 = [[SvTestObject alloc] init];
    [NSTimer scheduledTimerWithTimeInterval:5 target:testObject2 selector:@selector(timerAction:) userInfo:nil repeats:YES];
    [testObject2 release];
    NSLog(@"Invoke release to testObject2!");
}
复制代码

  上面的简单例子中,我们自定义了一个继承自NSObject的类SvTestObject,在这个类的init,dealloc和它的timerAction三个方法中分别打印信息。然后在appDelegate中分别测试一个单次执行的timer和一个重复执行的timer对方法接受者是否做了retain操作,因此我们在两种情况下都是shedule完timer之后立马对该测试对象执行release操作。

  测试单次执行的timer的结果如下:



 

  观察输出,我们会发现53分58秒的时候我们就对测试对象执行了release操作,但是知道54分03秒的时候timer触发完方法以后,该对象才实际的执行了dealloc方法。这就证明一次性的timer也会retain它的方法接收者,直到自己失效为之。

  测试重复性的timer的结果如下:



 

  观察输出我们发现,这个重复性的timer一直都在周期性的调用我们为它指定的方法,而且测试的对象也一直没有真正的被释放。

  通过以上小例子,我们可以发现在timer对它的接收者进行retain,从而保证了timer调用时的正确性,但是又引入了接收者的内存管理问题。特别是对于重复性的timer,它所引用的对象将一直存在,将会造成内存泄露。

  有问题就有应对方法,NSTimer提供了一个方法invalidate,让我们可以解决这种问题。不管是一次性的还是重复性的timer,在执行完invalidate以后都会变成无效,因此对于重复性的timer我们一定要有对应的invalidate。

  突然想起一种自欺欺人的写法,不知道你们有没有这么写过,我承认之前也有这样写过,哈哈,代码如下:

SvCheatYourself.m
复制代码
//
//  SvCheatYourself.m
//  SvTimerSample
//
//  Created by  maple on 12/19/12.
//  Copyright (c) 2012 maple. All rights reserved.
//
//  以下这种timer的用法,企图在dealloc中对timer进行invalidate是一种自欺欺人的做法
//  因为你的timer对self进行了retain,如果timer一直有效,则self的引用计数永远不会等于0

#import "SvCheatYourself.h"

@interface SvCheatYourself () {
    NSTimer *_timer;
}

@end

@implementation SvCheatYourself

- (id)init
{
    self = [super init];
    if (self) {
        _timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(testTimer:) userInfo:nil repeats:YES];
    }
    
    return self;
}

- (void)dealloc
{
    // 自欺欺人的写法,永远都不会执行到,除非你在外部手动invalidate这个timer
    [_timer invalidate];
    
    [super dealloc];
}

- (void)testTimer:(NSTimer*)timer
{
    NSLog(@"haha!");
}

@end//
//  SvCheatYourself.m
//  SvTimerSample
//
//  Created by  maple on 12/19/12.
//  Copyright (c) 2012 maple. All rights reserved.
//
//  以下这种timer的用法,企图在dealloc中对timer进行invalidate是一种自欺欺人的做法
//  因为你的timer对self进行了retain,如果timer一直有效,则self的引用计数永远不会等于0

#import "SvCheatYourself.h"

@interface SvCheatYourself () {
    NSTimer *_timer;
}

@end

@implementation SvCheatYourself

- (id)init
{
    self = [super init];
    if (self) {
        _timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(testTimer:) userInfo:nil repeats:YES];
    }
    
    return self;
}

- (void)dealloc
{
    // 自欺欺人的写法,永远都不会执行到,除非你在外部手动invalidate这个timer
    [_timer invalidate];
    
    [super dealloc];
}

- (void)testTimer:(NSTimer*)timer
{
    NSLog(@"haha!");
}

@end//
//  SvCheatYourself.m
//  SvTimerSample
//
//  Created by  maple on 12/19/12.
//  Copyright (c) 2012 maple. All rights reserved.
//
//  以下这种timer的用法,企图在dealloc中对timer进行invalidate是一种自欺欺人的做法
//  因为你的timer对self进行了retain,如果timer一直有效,则self的引用计数永远不会等于0

#import "SvCheatYourself.h"

@interface SvCheatYourself () {
    NSTimer *_timer;
}

@end

@implementation SvCheatYourself

- (id)init
{
    self = [super init];
    if (self) {
        _timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(testTimer:) userInfo:nil repeats:YES];
    }
    
    return self;
}

- (void)dealloc
{
    // 自欺欺人的写法,永远都不会执行到,除非你在外部手动invalidate这个timer
    [_timer invalidate];
    
    [super dealloc];
}

- (void)testTimer:(NSTimer*)timer
{
    NSLog(@"haha!");
}

@end
复制代码

  综上: timer都会对它的target进行retain,我们需要小心对待这个target的生命周期问题,尤其是重复性的timer。(NSTimer初始化后,self的retainCount加1。 那么,我们需要在释放这个类之前,执行[timer invalidate];否则,不会执行该类的dealloc方法。)

 

三、NSTimer会是准时触发事件吗

  答案是否定的,而且有时候你会发现实际的触发时间跟你想象的差距还比较大。NSTimer不是一个实时系统,因此不管是一次性的还是周期性的timer的实际触发事件的时间可能都会跟我们预想的会有出入。差距的大小跟当前我们程序的执行情况有关系,比如可能程序是多线程的,而你的timer只是添加在某一个线程的runloop的某一种指定的runloopmode中,由于多线程通常都是分时执行的,而且每次执行的mode也可能随着实际情况发生变化。

  假设你添加了一个timer指定2秒后触发某一个事件,但是签好那个时候当前线程在执行一个连续运算(例如大数据块的处理等),这个时候timer就会延迟到该连续运算执行完以后才会执行。重复性的timer遇到这种情况,如果延迟超过了一个周期,则会和后面的触发进行合并,即在一个周期内只会触发一次。但是不管该timer的触发时间延迟的有多离谱,他后面的timer的触发时间总是倍数于第一次添加timer的间隙。

  原文如下“A repeating timer reschedules itself based on the scheduled firing time, not the actual firing time. For example, if a timer is scheduled to fire at a particular time and every 5 seconds after that, the scheduled firing time will always fall on the original 5 second time intervals, even if the actual firing time gets delayed. If the firing time is delayed so far that it passes one or more of the scheduled firing times, the timer is fired only once for that time period; the timer is then rescheduled, after firing, for the next scheduled firing time in the future.”

  下面请看一个简单的例子:

Simulate Thread Busy
复制代码
- (void)applicationDidBecomeActive:(UIApplication *)application
{
    SvTestObject *testObject2 = [[SvTestObject alloc] init];
    [NSTimer scheduledTimerWithTimeInterval:1 target:testObject2 selector:@selector(timerAction:) userInfo:nil repeats:YES];
    [testObject2 release];
    
    NSLog(@"Simulate busy");
    [self performSelector:@selector(simulateBusy) withObject:nil afterDelay:3];
}

// 模拟当前线程正好繁忙的情况
- (void)simulateBusy
{
    NSLog(@"start simulate busy!");
    NSUInteger caculateCount = 0x0FFFFFFF;
    CGFloat uselessValue = 0;
    for (NSUInteger i = 0; i < caculateCount; ++i) {
        uselessValue = i / 0.3333;
    }
    NSLog(@"finish simulate busy!");
}
复制代码

  例子中首先开启了一个timer,这个timer每隔1秒调用一次target的timerAction方法,紧接着我们在3秒后调用了一个模拟线程繁忙的方法(其实就是一个大的循环)。运行程序后输出结果如下:



 

  观察结果我们可以发现,当线程空闲的时候timer的消息触发还是比较准确的,但是在36分12秒开始线程一直忙着做大量运算,知道36分14秒该运算才结束,这个时候timer才触发消息,这个线程繁忙的过程超过了一个周期,但是timer并没有连着触发两次消息,而只是触发了一次。等线程忙完以后后面的消息触发的时间仍然都是整数倍与开始我们指定的时间,这也从侧面证明,timer并不会因为触发延迟而导致后面的触发时间发生延迟。

  综上: timer不是一种实时的机制,会存在延迟,而且延迟的程度跟当前线程的执行情况有关。

 

四、NSTimer为什么要添加到RunLoop中才会有作用

  前面的例子中我们使用的是一种便利方法,它其实是做了两件事:首先创建一个timer,然后将该timer添加到当前runloop的default mode中。也就是这个便利方法给我们造成了只要创建了timer就可以生效的错觉,我们当然可以自己创建timer,然后手动的把它添加到指定runloop的指定mode中去。

  NSTimer其实也是一种资源,如果看过多线程变成指引文档的话,我们会发现所有的source如果要起作用,就得加到runloop中去。同理timer这种资源要想起作用,那肯定也需要加到runloop中才会又效喽。如果一个runloop里面不包含任何资源的话,运行该runloop时会立马退出。你可能会说那我们APP的主线程的runloop我们没有往其中添加任何资源,为什么它还好好的运行。我们不添加,不代表框架没有添加,如果有兴趣的话你可以打印一下main thread的runloop,你会发现有很多资源。 

  下面我们看一个小例子:  

复制代码
- (void)applicationDidBecomeActive:(UIApplication *)application
{
    [self testTimerWithOutShedule];
}

- (void)testTimerWithOutShedule
{
    NSLog(@"Test timer without shedult to runloop");
    SvTestObject *testObject3 = [[SvTestObject alloc] init];
    NSTimer *timer = [[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:1] interval:1 target:testObject3 selector:@selector(timerAction:) userInfo:nil repeats:NO];
    [testObject3 release];
    NSLog(@"invoke release to testObject3");
}

- (void)applicationWillResignActive:(UIApplication *)application
{
    NSLog(@"SvTimerSample Will resign Avtive!");
}
复制代码

  这个小例子中我们新建了一个timer,为它指定了有效的target和selector,并指出了1秒后触发该消息,运行结果如下:



 

  观察发现这个消息永远也不会触发,原因很简单,我们没有将timer添加到runloop中。

  综上: 必须得把timer添加到runloop中,它才会生效。


五、NSTimer加到了RunLoop中但迟迟的不触发事件

  为什么明明添加了,但是就是不按照预先的逻辑触发事件呢???原因主要有以下两个:

1、runloop是否运行

  每一个线程都有它自己的runloop,程序的主线程会自动的使runloop生效,但对于我们自己新建的线程,它的runloop是不会自己运行起来,当我们需要使用它的runloop时,就得自己启动。

  那么如果我们把一个timer添加到了非主线的runloop中,它还会按照预期按时触发吗?下面请看一段测试程序:

复制代码
- (void)applicationDidBecomeActive:(UIApplication *)application
{
    [NSThread detachNewThreadSelector:@selector(testTimerSheduleToRunloop1) toTarget:self withObject:nil];
}

// 测试把timer加到不运行的runloop上的情况
- (void)testTimerSheduleToRunloop1
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    
    NSLog(@"Test timer shedult to a non-running runloop");
    SvTestObject *testObject4 = [[SvTestObject alloc] init];
    NSTimer *timer = [[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:1] interval:1 target:testObject4 selector:@selector(timerAction:) userInfo:nil repeats:NO];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
    // 打开下面一行输出runloop的内容就可以看出,timer却是已经被添加进去
    //NSLog(@"the thread's runloop: %@", [NSRunLoop currentRunLoop]);
    
    // 打开下面一行, 该线程的runloop就会运行起来,timer才会起作用
    //[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:3]];
    
    [testObject4 release];
    NSLog(@"invoke release to testObject4");

    [pool release];
}

- (void)applicationWillResignActive:(UIApplication *)application
{
    NSLog(@"SvTimerSample Will resign Avtive!");
}
复制代码

  上面的程序中,我们新创建了一个线程,然后创建一个timer,并把它添加当该线程的runloop当中,但是运行结果如下:



 

  观察运行结果,我们发现这个timer知道执行退出也没有触发我们指定的方法,如果我们把上面测试程序中“//[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:3]];”这一行的注释去掉,则timer将会正确的掉用我们指定的方法。

2、mode是否正确

  我们前面自己动手添加runloop的时候,可以看到有一个参数runloopMode,这个参数是干嘛的呢?

  前面提到了要想timer生效,我们就得把它添加到指定runloop的指定mode中去,通常是主线程的defalut mode。但有时我们这样做了,却仍然发现timer还是没有触发事件。这是为什么呢?

  这是因为timer添加的时候,我们需要指定一个mode,因为同一线程的runloop在运行的时候,任意时刻只能处于一种mode。所以只能当程序处于这种mode的时候,timer才能得到触发事件的机会。

  举个不恰当的例子,我们说兄弟几个分别代表runloop的mode,timer代表他们自己的才水桶,然后一群人去排队打水,只有一个水龙头,那么同一时刻,肯定只能有一个人处于接水的状态。也就是说你虽然给了老二一个桶,但是还没轮到它,那么你就得等,只有轮到他的时候你的水桶才能碰上用场。

  最后一个例子我就不贴了,也很简单,需要的话,我qq发给你。

  综上: 要让timer生效,必须保证该线程的runloop已启动,而且其运行的runloopmode也要匹配。

 

 -------------------------------------------------------------------------------------------------------------

 

创建一个 Timer

  • + scheduledTimerWithTimeInterval: invocation: repeats:
  • + (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti   invocation:(NSInvocation *)invocation   repeats:(BOOL)yesOrNo;
  • + scheduledTimerWithTimeInterval: target: selector: userInfo: repeats:
  • + (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti   target:(id)aTarget   selector:(SEL)aSelector   userInfo:(id)userInfo   repeats:(BOOL)yesOrNo;
  • 创建返回一个新的NSTimer对象和时间表,在当前的默认模式下循环调用一个实例方法。
  • + timerWithTimeInterval: invocation: repeats:
  • + (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
  • + timerWithTimeInterval: target:selector: userInfo:repeats:
  • + (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo;
  • – initWithFireDate: interval: target: selector: userInfo: repeats:
  • - (id)initWithFireDate:(NSDate *)date   interval:(NSTimeInterval)ti     target:(id)t    selector:(SEL)s    userInfo:(id)ui    repeats:(BOOL)rep;

 

 

scheduledTimerWithTimeInterval:(NSTimeInterval)seconds  

预订一个Timer,设置一个时间间隔。

表示输入一个时间间隔对象,以秒为单位,一个>0的浮点类型的值,如果该值<0,系统会默认为0.1

 target:(id)aTarget

表示发送的对象,如self

 selector:(SEL)aSelector

方法选择器,在时间间隔内,选择调用一个实例方法

userInfo:(id)userInfo

此参数可以为nil,当定时器失效时,由你指定的对象保留和释放该定时器。

repeats:(BOOL)yesOrNo

YES时,定时器会不断循环直至失效或被释放,当NO时,定时器会循环发送一次就失效。

invocation:(NSInvocation *)invocation

 

 

 

 

启动 Timer

  • – fire

停止 Timer

  • – invalidate

Timer设置

  • – isValid
  • – fireDate
  • – setFireDate:
  • – timeInterval
  • – userInfo
 

NSTimeInterval类:是一个浮点数字,用来定义秒

 

 

 

PS:

NSTimer其实是将一个监听加入的系统的RunLoop中去,当系统runloop到如何timer条件的循环时,会调用timer一次,当timer执行完,也就是回调函数执行之后,timer会再一次的将自己加入到runloop中去继续监听。

 CFRunLoopTimerRef 和 NSTimer这两个类型是可以互换的, 当我们在传参数的时候,看到CFRunLoopTimerRef可以传NSTimer的参数,增加强制转化来避免编译器的警告信息

 

 

指定(注册)一个timer到 RunLoops中, 一个timer对象只能够被注册到一个runloop中,在同一时间,在这个runloop中它能够被添加到多个runloop中模式中去。

 

有以下三种方法:

 使用 scheduledTimerWithTimeInterval:invocation:repeats: 或者scheduledTimerWithTimeInterval:target:selector:userInfo:repeats: 这两个类方法创建一个timer并把它指定到一个默认的runloop模式中

 使用 timerWithTimeInterval:invocation:repeats: 或者 timerWithTimeInterval:target:selector:userInfo:repeats:这两个类方法创建一个timer的对象,不把它知道那个到run loop. (当创建之后,你必须手动的调用NSRunLoop下对应的方法 addTimer:forMode: 去将它制定到一个runloop模式中.)

 使用 initWithFireDate:interval:target:selector:userInfo:repeats: 方法分配并创建一个NSTimer的实例 (当创建之后,你必须手动的调用NSRunLoop下对应的方法 addTimer:forMode: 去将它制定到一个runloop模式中.)

 

C代码   收藏代码
  1. self.m_timerTimeOut = [NSTimer scheduledTimerWithTimeInterval:1.0  
  2.                                                            target:self  
  3.                                                          selector:@selector(handleTimeOutAction)  
  4.                                                          userInfo:nil repeats:YES];  

第一个参数,1.0代表1秒,如果该值<0,系统会默认为0.1
第二个参数, target:(id)aTarget,表示发送的对象,如self
第三个参数,selector:(SEL)aSelector方法选择器,在时间间隔内,选择调用一个实例方法
第四个参数,userInfo:(id)userInfo此参数可以为nil,当定时器失效时,由你指定的对象保留和释放该定时器。
第五个参数,repeats:(BOOL)yesOrNo当YES时,定时器会不断循环直至失效或被释放,当NO时,定时器会循环发送一次就失效。

例如:
C代码   收藏代码
  1. if ([m_timerTimeOut isValid])  
  2.     {  
  3.         [m_timerTimeOut invalidate];  
  4.         m_timerTimeOut = nil;  
  5.     }  
  6. //把定时器添加到当前runloop中,用于解决在定时器启动时,其他的手势操作对定时器的影响  
  7. [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];  

检查状态,如果是开始状态,那么制空m_timerTimeOut这个Timer,如果你问我为何设个nil,因为这样可以保证这个timer马上为nil,哈哈~安全起见,保证一个timer在跑
NSRunLoop这里建议添加此句,因为有一种情况是一个UILable用来显示时间,其又在一个UIView上,如果现在view接收手势滑动等事件,此时timer会停止几秒,造成不良影响





invalidate和release内存问题转自 冷冰雨之家
iphone NSTimer invalidate 和 release 释放问题
最近在使用NSTimer的时候,遇到了一些内存错误的问题,找了一片很好的文章可惜是英文的,现自己翻译出来,以备后用。
原文:
[timer release] only needs to be called if you “own” the timer. From Apple’s documentation:
Because the run loop maintains the timer, from the perspective of memory management there’s typically no need to keep a reference to a timer once you’ve scheduled it. Since the timer is passed as an argument when you specify its method as a selector, you can invalidate a repeating timer when appropriate within that method. In many situations, however, you also want the option of invalidating the timer—perhaps even before it starts. In this case, you do need to keep a reference to the timer, so that you can send it an invalidate message whenever is appropriate. If you create an unscheduled timer (see “Unscheduled Timers”), then you must maintain a strong reference to the timer (in a reference-counted environment, you retain it) so that it is not deallocated before you use it.
What does this mean?
If you alloc and init a timer, you must also release it, like so:
NSTimer * timer = [[NSTimer alloc] initWith…];
NSRunLoop * runLoop = [NSRunLoop currentRunLoop];

[runLoop addTimer:timer forMode:NSDefaultRunLoopMode];

[timer release];
……
[timer invalidate];

timer = nil;

Once the timer has been added to the run loop, there is no reason to keep a reference to it anymore, since the run loops owns it. In this case, as shown, you would release the timer as soon as you add it to the run loop, and then simplyinvalidate it when you are finished. The final line (setting timer to nil) is for safety. The call to invalidate will result in the timer being released (by the run loop), so it is unsafe to keep a reference that points to it. Setting the local reference to nil keeps things kosher.
If, however, you create a timer using one of the convenience methods like so:
NSTimer * timer = [NSTimer scheduledTimerWithTimeInterval ...];

You do not need to call [timer release] at all!

The convenience method adds the timer to the run loop, which then owns it, so you do not need to perform any memory management on the returned timer object. You would simplyinvalidate the timer when you no longer want to use it:
[timer invalidate];timer = nil;

Or, if the timer was not set to repeat, you would do absolutely nothing at all, since it would be released after its first invocation.

译文:[timer release]这个方法只有在当你拥有timer的时候才可以调用。根据Apple的文档描述如下:由于RunLoop保持着timer。从内存管理的角度上看,当我们scheduled(预定)一个timer的时候,我们通常是不需要保持它的reference(引用计数)的。由于timer是作为一个参数传递的,timer的指定的方法是selector的形式,所以你可以在指定的方法的内部,在合适的时候invalidate一个重复的timer(repeating timer)。然而,在许多的场合下,你可能会想在timer开始之前就invalidating timer。在这种情况下,又必须保持一个对timer的引用(reference),以便于你可以合适的时间向timer发送invalidate消息。如果你创建了一个 unscheduled timer,你必须保持一个对于这个timer的 strong reference(在 reference_count(引用计数的环境下),你给timer发送了一个retain的消息),n这样做目的是保证在你使用该timer之前,它不会被deallocated。简言之:如果你allloc init了一个timer,你必须release it,例如:
NSTimer * timer = [[NSTimer alloc] initWith…];
NSRunLoop * runLoop = [NSRunLoop currentRunLoop];
[runLoop addTimer:timer forMode:NSDefaultRunLoopMode];
[timer release];
……
[timer invalidate];
timer = nil;
一旦timer被加入了Runloop,我们就没有任何原因来保持一个引用,因为Runloop会保持它。在这个展示中,正如我们我展示的,你应该release 掉它,当你把它添加进Runloop的时候,并且像示例中的那样在timer结束的时候 invalidate 。最后一行代码(timer = nil)是为了安全。invalidate 的调用会使得timer被release(由Runloop控制),所以保持一个指向timer的引用是不安全。将本地的引用设置为空是符合规则的。
无论如何,如果你创建一个timer按照下面的这种形式:
NSTimer * timer = [NSTimer scheduledTimerWithTimeInterval ...];
你根本不需要 调用 [timer release]!
这个便捷的方法,把timer加入到了Runloop,Runloop会保持timer的引用计数,所以你不需要在返回的timer对象上实现任何的
内存管理。你应该想示例的那样invalidate timer,如下:
[timer invalidate];timer = nil;
或者,如果timer没有设置重复(repeat),你就安心的不需要做任何的事情,因为他会在第一次调用完成后release timer。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值