NSTimer的详细总结

先说一下我的业务需求,最近在做一个小项目,需要用到定时器的功能,NSTimer类,期间,出现了一些小问题,不过最终通过自己的努力,终于做出来了。我想总结一下,我对NSTimer类的学习和理解。

不多说了,先上效果图


界面元素很简单,两个UIButton 开始和暂停,20表示起始倒计时。最终的效果是,按开始按钮的时候,倒计时开始运行,按暂停按钮的时候,计时器,停止倒计时。当倒计时为0的时候,弹出一个对话框,提示时间已到。


业务需求很简单,但是,在我的实现中,却出现了,一些小错误。 主要是暂停键不能点击多次,开始键也不能点击多次,我相信,刚开始,接触这个NSTimer的人,也会出现这几个问题。


直接上几个主要的代码:

控制器类的.h文件中

@interface sdsViewController : UIViewController<UIAlertViewDelegate>

//定义一个定时器,做为实例变量
@property(nonatomic,retain) NSTimer *timer;

//显示倒计时当前状态
@property (retain, nonatomic) IBOutlet UILabel *timeDisplay;

//开始按钮,响应的action
- (IBAction)startTime:(id)sender;

//暂停按钮响应的action
- (IBAction)stopTime:(id)sender;

@end



.m中关键代码

开始按钮 响应代码

- (IBAction)startTime:(id)sender {


 //如果定时器对象不存在,则创建一个并启动
    
    if(!_timer){
        
        //创建一个定时器,这个是直接加到当前消息循环中,注意与其他初始化方法的区别
       _timer=[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(changeTimeAtTimedisplay) userInfo:nil repeats:YES]; 


        //  [_timer fire]; //对于这个fire方法,稍后会详解,它不是启动一个定时器,这么简单
        
    }


}

//结束按钮响应代码:


- (IBAction)stopTime:(id)sender {
    
    if (_timer) {
        NSLog(@"调用 self.time为真!!");


            //如果定时器在运行
        if ([self.timer isValid]) {


            NSLog(@"单击停止按钮,取消定时器!!");
        
            [self.timer invalidate];

        //这行代码很关键
           _timer=nil;

            
        }
        
    }
    
}

一切OK,现在分析程序用到的关键地方。

先看看NSTimer类的结构,比较简单

Tasks

Creating a Timer

//创建一个定时器 ,以下是便利构造器方法,都懂的
+ scheduledTimerWithTimeInterval:invocation:repeats:
+ scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:

+ timerWithTimeInterval:invocation:repeats:
+ timerWithTimeInterval:target:selector:userInfo:repeats:

//初始化方法
– initWithFireDate:interval:target:selector:userInfo:repeats:


//是开始一个定时器吗,恐怕没那么简单????????
Firing a Timer
– fire

//是暂停一个定时器吗,NO ,是Stop ,写的很清楚

Stopping a Timer
– invalidate

//关于定时器的以下信息
Information About a Timer
– isValid  //是否在运行
– fireDate //Returns the date at which the receiver will fire.
– setFireDate: //重新设置定时器开始运行的时间
– timeInterval  //定时器延时时间

– userInfo //其他信息


先说一下,初始化方法

+ scheduledTimerWithTimeInterval:invocation:repeats:
+ scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:

这两个是创建一个定时器,并加入到当前运行循环中,即我们可以这样去理解,这样初始化一个定时器时,在(NSTimeInterval)seconds 时间之后,自动启动定时器。


而以下两个初始化方法这不一样:

+ timerWithTimeInterval:invocation:repeats:
+ timerWithTimeInterval:target:selector:userInfo:repeats:


这两个同样是创建,但没有加入到,运行循环中。class method to create the timer object without scheduling it on a run loop.然后,建立之后,必须(after creating it, you must add the timer to a run loop manually by calling the addTimer:forMode: method of the corresponding NSRunLoop object.),这就是与上面两个方法的区别。

  //创建一个定时器
       _timer=[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(changeTimeAtTimedisplay) userInfo:nil repeats:YES];




_timer=[NSTimer timerWithTimeInterval:10 target:self selector:@selector(changeTimeAtTimedisplay) userInfo:nil repeats:YES];
       //必须手动加入到当前循环中去
       NSRunLoop *runloop=[NSRunLoop currentRunLoop];
       [runloop addTimer:_timer forMode:NSDefaultRunLoopMode];


以上两端代码效果是一样的



关于这个方法:

Firing a Timer
– fire


其实他并不是真的启动一个定时器,从之前的初始化方法中我们也可以看到,建立的时候,在适当的时间,定时器就会自动启动。那这个方法是干什么的呢。

You can use this method to fire a repeating timer without interrupting its regular firing schedule. If the timer is non-repeating, it is automatically invalidated after firing, even if its scheduled fire date has not arrived.

这是官方文档的说法,英文说的很清楚,但我们理解还不是很到位,为了彻底搞懂它的功能。我又做了一个测试。


也是很简单,把上面那个定时器,改变一点

//初始化的时候创建一个定时器

- (id) initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{
    
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    
    if (self) {
        // Custom initialization
        
        //创建一个定时器,
        
       _timer=[NSTimer timerWithTimeInterval:10 target:self selector:@selector(changeTimeAtTimedisplay) userInfo:nil repeats:YES];
      

//手动加入到循环中

      NSRunLoop *runloop=[NSRunLoop currentRunLoop];

 [runloop addTimer:_timer forMode:NSDefaultRunLoopMode];


//当然这个定时器会自动启动,只不多过了十秒之后,才触发

}

return self

}



当我们单击“开始”按钮时,


- (IBAction)startTime:(id)sender {
   
    //只是简单地调用一下这个方法,看到底功能是什么
  [_timer fire];

}

结果是,单击一下按钮,倒计时减1,单击一下减1,即它把触发的时间给提前了,但过十秒后倒计时还会减1,即它只是提前触发定时器,而不影响之前的那个定时器设置的时间,就好比我们等不及要去看一场球赛,赶紧把车开快些一样,fire的功能就像让我们快些到球场,但却不影响球赛开始的时间。

还记得之前那个初始化定时器时,设置的是YES吗,当我们,改为NO时,即不让它循环触发时,我们此时再单击开始按钮。会猛然发现,倒计时减1了,但当我们再点击开始按钮时,会发现倒计时,不会动了。原因是:我们的定时器,被设置成只触发一次,再fire的时候,触发一次,该定时器,就被自动销毁了,以后再fire也不会触发了。


现在 我们再看官方的解释,或许就会更明白了,



You can use this method to fire a repeating timer without interrupting its regular firing schedule. If the timer is non-repeating, it is automatically invalidated after firing, even if its scheduled fire date has not arrived.

这就对了,fire并不是启动一个定时器,只是提前触发而已。


上一篇的时候我们弄懂了 fire的用法,知道,它并不是启动一个定时器

继续讨论UITime类的其他方法:

Stopping a Timer
– invalidate

这个方法很容易让人误解,很容易认为他是让定时器暂停(语文没学好,或是英语没学好,嘿嘿),

官方的解释是:

This method is the only way to remove a timer from an NSRunLoop object. The NSRunLoop object removes and releases the timer, either just before the invalidate method returns or at some later point.

If it was configured with target and user info objects, the receiver releases its references to those objects as well.

意思是说:这是唯一一个把一个定时器从一个运行循环中移除的方法。NSRunLoop object这个对象移除,并且release掉这个的定时器,或者是在这个invalidate方法返回的之前或是在之后的某个时间段,再进行移除并release操作。


至此对于这个NSTimer类的学习,基本上,算是差不多了,一些基本功能能够实现了。至于深入,以后有项目需要的时候,再做深入研究吧。我感觉学习就是一个不断出错,不断学习,不断总结的简单事情,我其实把总结看的很重要,看似解决了一个问题,其实是一类,一大类问题,多花些时间也是值得地。我想以后在遇见此类问题,就比较有话语权,能够说的滔滔不绝。也会有解决问题的积累,举一反三。


对于这个项目,我不但学会了,弄清楚了,NSTimer这一个类的使用方法,而且让我对官方文档的严谨性,透彻性,有了进一步了解。有事没事多看API是一个好习惯。此次也是没有理解,英语的一些说明,才导致了一些小错误,再次感叹英语真的很重要啊!!!!!!!

由这个项目我也学会了内存管理的一些技巧,


- (IBAction)stopTime:(id)sender {


//如果_timer不等于nil   

    if (_timer) {
        NSLog(@"调用 self.time为真!!");
// 如果定时器在运行,我们就invalidate


//        if ([self.timer isValid]) {
//            NSLog(@"单击停止按钮,取消定时器!!");
//
//           [self.timer invalidate];
//           同时把他置为nil
//          _timer=nil; //这就是我要说的一个对象release和对象=nil的区别,内存管理
//            
//        }
     

//不加上面这个if判断也可以用下面这个,效果是一样的

        [self.timer invalidate];
        _timer=nil;//这个很重要

        
    }

}


关于对象release和对象=nil的区别,网上也有很多讨论,我也实际验证了一下,


个人认为  [对象 release] 后 这个对象就没有指向任何内存空间了,这个时候,它是不能被使用的(可以试试  NSLog 一下  release 后的对象,有惊喜的。。。)
当 对象 = nil 时,这个对象是指向了一个内存空间的( 0x0000,即开始地址),这个时候这个对象是可以被NSLog出来的。。。。。


如果一个对象alloc 一次并release后,以后是不能再使用的,它虽然也指向原来的内存空间,但其内容是不确定的,使用的话,它的内容有时候没事,但有时候会崩溃的。


下面这个例子可以很好地演示这个过程。


//我创建一个字符串对象

 NSString *string=[[NSString alloc] initWithFormat:@"henaho"];

        NSLog(@"对象release前的地址为:%p",string);
        NSLog(@"字符串内容为:%@",string);
//        string=nil;

     //使用之后我release

      [string release];
//       string=nil;
        
        if (string) {
            NSLog(@"release后对象指针不为nil");
        }else{
            
            NSLog(@"release后对象指针为nil"); 
        }
        NSLog(@"对象release后的内容为:%@",string);
        NSLog(@"对象release后的地址为:%p",string);


程序运行结果是: 啊 啊啊啊啊啊啊 这不是有正确结果吗????




我们在运行一次试试!!!!!!!崩溃啦啦啦!!!!












但当我们在release后一个对象后,将其=nil时,下面再用到的时候就不会出错了。


   [string release];
      string=nil;


看结果,是不是很神奇!!!!!





综上所述:一个好的编程习惯是,

[对象 release]; 

对象=nil;

这是一个好的作风,以后万一用到的时候也不会崩掉。自己的水平目

前就这么高,高深的也理解不了。慢慢进步……


好了,有一个定时器引发的血案终于可以结案了,开始研究NSTimer的时候感觉问题没这么多,但自己就是一个不断发现问题,并解决问题的人,看我终于解决了一些关键的问题,感觉自己的编程能力有了一个很大的提高,至少解决问题的能力,有了突飞猛进。学习的信心和动力一下子,呵呵……如果有哪位看到了,感觉有疑惑,我们可以一起学习,一起进步,一起解决问题的乐趣是无穷的。哈哈,不枉费我打这么多字和花费这么多时间来总结,我知道,在某一天,肯定会

受益的,当然受益的不仅仅是自己……


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值