定时器的使用

一. NSTimer

NSTimer的初始化方法有以下几种:

会自动启动, 并加入* MainRunloop** NSDefaultRunLoopMode*中,

注意: 这里的自动启动, 并不是马上就会启动, 而是会延迟大概一个interval的时间:

+ (NSTimer*)scheduledTimerWithTimeInterval:(NSTimeInterval)intervalrepeats:(BOOL)repeatsblock:(void(^)(NSTimer*timer))block

1

参数:

- internal : 时间间隔, 多久调用一次

- repeats: 是否重复调用

- block: 需要重复做的事情

使用:

[NSTimer scheduledTimerWithTimeInterval:1repeats:YESblock:^(NSTimer * _Nonnull timer) {staticNSIntegernum =0;NSLog(@"%ld", (long)num);        num++;if(num >4) {            [timer invalidate];NSLog(@"end");        }    }];NSLog(@"start");

这时, 控制台的输出:

2016-12-2916:29:53.901定时器[11673:278678]start2016-12-2916:29:54.919定时器[11673:278678]02016-12-2916:29:55.965定时器[11673:278678]12016-12-2916:29:56.901定时器[11673:278678]22016-12-2916:29:57.974定时器[11673:278678]32016-12-2916:29:58.958定时器[11673:278678]42016-12-2916:29:58.959定时器[11673:278678]end

可以看出, 这里的internal设置为1s, 大概延迟了1s才开始执行block里的内容;

这里的停止定时器, 我直接在block里进行的, 如果使用一个全局变量来再其他地方手动停止定时器,需要这样进行:

[self.timer invalidate];self.timer =nil;

1

2

+ (NSTimer*)scheduledTimerWithTimeInterval:(NSTimeInterval)tiinvocation:(NSInvocation*)invocationrepeats:(BOOL)yesOrNo

1

参数:

- ti: 重复执行时间间隔

- invocation: NSInvocation实例, 其用法见NSInvocation的基本用法

- yesOrNo: 是否重复执行

示例:

// NSInvocation形式- (void)timer2 {    NSMethodSignature *method = [ViewController instanceMethodSignatureForSelector:@selector(invocationTimeRun:)];    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:method];    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0invocation:invocation repeats:YES];// 设置方法调用者invocation.target=self;// 这里的SEL需要和NSMethodSignature中的一致invocation.selector=@selector(invocationTimeRun:);// 设置参数// //这里的Index要从2开始,以为0跟1已经被占据了,分别是self(target),selector(_cmd)// 如果有多个参数, 可依次设置3 4 5 ...[invocation setArgument:&timer atIndex:2];    [invocation invoke];NSLog(@"start");}- (void)invocationTimeRun:(NSTimer *)timer {staticNSIntegernum =0;NSLog(@"%ld---%@", (long)num, timer);    num++;if(num >4) {        [timer invalidate];    }}

输出:

2016-12-2916:52:54.029定时器[12089:289673]0---<__NSCFTimer: 0x60000017d940>2016-12-2916:52:54.029定时器[12089:289673]start2016-12-2916:52:55.104定时器[12089:289673]1---<__NSCFTimer: 0x60000017d940>2016-12-2916:52:56.095定时器[12089:289673]2---<__NSCFTimer: 0x60000017d940>2016-12-2916:52:57.098定时器[12089:289673]3---<__NSCFTimer: 0x60000017d940>2016-12-2916:52:58.094定时器[12089:289673]4---<__NSCFTimer: 0x60000017d940>

可以看出, 这里定时器是立马就执行了, 没有延迟;

此方法可以传递多个参数, 下面是传递两个参数的示例:

// NSInvocation形式- (void)timer2 {    NSMethodSignature *method = [ViewController instanceMethodSignatureForSelector:@selector(invocationTimeRun:des:)];    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:method];    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0invocation:invocation repeats:YES];// 设置方法调用者invocation.target=self;// 这里的SEL需要和NSMethodSignature中的一致invocation.selector=@selector(invocationTimeRun:des:);// 设置参数// //这里的Index要从2开始,以为0跟1已经被占据了,分别是self(target),selector(_cmd)// 如果有多个参数, 可依次设置3 4 5 ...[invocation setArgument:&timer atIndex:2];// 设置第二个参数NSString*dsc = @"第二个参数是字符串";    [invocation setArgument:&dsc atIndex:3];    [invocation invoke];NSLog(@"start");}- (void)invocationTimeRun:(NSTimer *)timer des:(NSString*)dsc {staticNSIntegernum =0;NSLog(@"%ld---%@--%@", (long)num, timer, dsc);    num++;if(num >4) {        [timer invalidate];    }}

输出:

2016-12-2916:57:45.087定时器[12183:292324]0---<__NSCFTimer: 0x60000016dbc0>--第二个参数是字符串2016-12-2916:57:45.088定时器[12183:292324]start2016-12-2916:57:46.161定时器[12183:292324]1---<__NSCFTimer: 0x60000016dbc0>--第二个参数是字符串2016-12-2916:57:47.161定时器[12183:292324]2---<__NSCFTimer: 0x60000016dbc0>--第二个参数是字符串2016-12-2916:57:48.150定时器[12183:292324]3---<__NSCFTimer: 0x60000016dbc0>--第二个参数是字符串2016-12-2916:57:49.159定时器[12183:292324]4---<__NSCFTimer: 0x60000016dbc0>--第二个参数是字符串

+ (NSTimer*)scheduledTimerWithTimeInterval:(NSTimeInterval)titarget:(id)aTargetselector:(SEL)aSelectoruserInfo:(nullableid)userInforepeats:(BOOL)yesOrNo

1

参数:

- ti: 时间间隔

- aTarget: 调用者

- aSelector: 执行的方法

- userInfo: 参数

- yesOrNo: 是否重复执行

示例:

- (void)timer3 {    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1target:selfselector:@selector(targetRun:) userInfo:@"这是携带的参数"repeats:YES];NSLog(@"start");}- (void)targetRun:(NSTimer *)timer {staticNSIntegernum =0;NSLog(@"%ld---%@--%@", (long)num, timer, timer.userInfo);    num++;if(num >4) {        [timer invalidate];    }}


输出:

2016-12-2917:05:11.590定时器[12328:296879]start2016-12-2917:05:12.655定时器[12328:296879]0---<__NSCFTimer: 0x608000162700>--这是携带的参数2016-12-2917:05:13.661定时器[12328:296879]1---<__NSCFTimer: 0x608000162700>--这是携带的参数2016-12-2917:05:14.664定时器[12328:296879]2---<__NSCFTimer: 0x608000162700>--这是携带的参数2016-12-2917:05:15.651定时器[12328:296879]3---<__NSCFTimer: 0x608000162700>--这是携带的参数2016-12-2917:05:16.650定时器[12328:296879]4---<__NSCFTimer: 0x608000162700>--这是携带的参数


下面这三种方式创建定时器的用法, 和上面相应的方法类似, 需要注意的是, 这样创建的定时器, 并不会执行, 需要我们手动来开启定时器;

+ (NSTimer*)timerWithTimeInterval:(NSTimeInterval)intervalrepeats:(BOOL)repeatsblock:(void(^)(NSTimer*timer))block+ (NSTimer*)timerWithTimeInterval:(NSTimeInterval)titarget:(id)aTargetselector:(SEL)aSelectoruserInfo:(nullableid)userInforepeats:(BOOL)yesOrNo+ (NSTimer*)timerWithTimeInterval:(NSTimeInterval)tiinvocation:(NSInvocation*)invocationrepeats:(BOOL)yesOrNo


开启的方式是, 将当前定时器添加到RunLoop中:

[[NSRunLoop currentRunLoop]addTimer:timerforMode:NSDefaultRunLoopMode];

1

下面给出一个示例:

- (void)timer4 {    NSTimer *timer = [NSTimer timerWithTimeInterval:1repeats:YESblock:^(NSTimer * _Nonnull timer) {staticNSIntegernum =0;NSLog(@"%ld", (long)num);        num++;if(num >4) {            [timer invalidate];            timer =nil;NSLog(@"end");        }    }];    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];NSLog(@"start");}


输出:

2016-12-2917:12:13.955定时器[12498:301751]start2016-12-2917:12:15.013定时器[12498:301751]02016-12-2917:12:16.018定时器[12498:301751]12016-12-2917:12:17.011定时器[12498:301751]22016-12-2917:12:18.024定时器[12498:301751]32016-12-2917:12:19.023定时器[12498:301751]42016-12-2917:12:19.023定时器[12498:301751]end


定时器基本的创建方式就这些了, 还可以设置其他的属性, 例如开启时间, 这些直接参考其API进行设置即可;

注意: 以上实例中, 我没有使用全局的NSTimer对象, 如果设置全局变量, 或者设置为属性, 在停止定时器的时候要手动置为nil, 即:

[timerinvalidate];timer= nil;

二. GCD

dispatch_after : 延迟执行一次

dispatch_after(dispatch_time_twhen, dispatch_queue_t queue, dispatch_block_tblock)

示例:

-(void)gcdTimer {    // 延迟2s    dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW,2*NSEC_PER_SEC);    dispatch_after(delayTime,dispatch_get_main_queue(), ^(void){        NSLog(@"延迟2s后执行");    });    NSLog(@"start");}


重复执行的定时器

voiddispatch_source_set_timer(dispatch_source_tsource,    dispatch_time_t start,    uint64_t interval,    uint64_t leeway)


参数:

- source: 定时器

- start: 开始时间, 当我们使用dispatch_time或者DISPATCH_TIME_NOW时,系统会使用默认时钟来进行计时。然而当系统休眠的时候,默认时钟是不走的,也就会导致计时器停止。使用dispatch_walltime可以让计时器按照真实时间间隔进行计时;

- interval: 间隔(如果设置为 DISPATCH_TIME_FOREVER 则只执行一次)

- leeway: 允许的误差范围; 计时不可能是百分百精确的, 即使设置为0, 也不是百分百精确的, 所以可以设置合理的允许误差, 单位: 纳秒(NSEC_PER_SEC)

相关内容, 可参考文章:Dispatch Source Timer 的使用以及注意事项

// 重复执行的定时器- (void)gcdTimer1 {// 获取全局队列dispatch_queue_tqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);// 创建定时器dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,0,0, queue);// 开始时间dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0* NSEC_PER_SEC));//    dispatch_time_t start = dispatch_walltime(NULL, 0);// 重复间隔uint64_t interval = (uint64_t)(1.0* NSEC_PER_SEC);// 设置定时器dispatch_source_set_timer(_timer, start, interval,0);// 设置需要执行的事件dispatch_source_set_event_handler(_timer, ^{//在这里执行事件staticNSIntegernum =0;NSLog(@"%ld", (long)num);        num++;if(num >4) {NSLog(@"end");// 关闭定时器dispatch_source_cancel(_timer);        }    });// 开启定时器dispatch_resume(_timer);NSLog(@"start");}


输出:

2016-12-3010:15:01.114定时器[3393:99474]start2016-12-3010:15:02.187定时器[3393:99796]02016-12-3010:15:03.114定时器[3393:99796]12016-12-3010:15:04.186定时器[3393:99796]22016-12-3010:15:05.188定时器[3393:99796]32016-12-3010:15:06.188定时器[3393:99796]42016-12-3010:15:06.188定时器[3393:99796]end


这里的开始时间设置了1s的间隔, 所以1s之后才开始执行,可以设置使用DISPATCH_TIME_NOW来立马执行;

注意:

这里的开始时间(start)可以使用下面的方式的来设置:

dispatch_time_tstart= dispatch_walltime(NULL,0);

1

或者直接设置为:* DISPATCH_TIME_NOW*

关于* dispatch_walltime** dispatch_time*的区别, 上面也有提及,也可参考stackOverflow上的这个回答; 主要区别就是前者在系统休眠时还会继续计时, 而后者在系统休眠时就停止计时, 待系统重新激活时, 接着继续计时;

停止计时器:

停止GCD定时器的方式,Dispatch Source Timer 的使用以及注意事项中有提及, 主要有以下两种:

// 关闭定时器// 完全销毁定时器, 重新开启的话需要重新创建// 全局变量, 关闭后需要置为nildispatch_source_cancel(_timer);// 暂停定时器// 可使用dispatch_resume(_timer)再次开启// 全局变量, 暂停后不能置为nil, 否则不能重新开启dispatch_suspend(_timer);


三. CADisplayLink

CADisplayLink默认每秒运行60次,通过它的* frameInterval*属性改变每秒运行帧数,如设置为2,意味CADisplayLink每隔一帧运行一次,有效的逻辑每秒运行30

屏幕刷新时调用:CADisplayLink是一个能让我们以和屏幕刷新率同步的频率将特定的内容画到屏幕上的定时器类。CADisplayLink以特定模式注册到runloop后,每当屏幕显示内容刷新结束的时候,runloop就会向CADisplayLink指定的target发送一次指定的selector消息, CADisplayLink类对应的selector就会被调用一次。所以通常情况下,按照iOS设备屏幕的刷新率60次/秒

延迟:iOS设备的屏幕刷新频率是固定的,CADisplayLink在正常情况下会在每次刷新结束都被调用,精确度相当高。但如果调用的方法比较耗时,超过了屏幕刷新周期,就会导致跳过若干次回调调用机会。

如果CPU过于繁忙,无法保证屏幕60次/秒的刷新率,就会导致跳过若干次调用回调方法的机会,跳过次数取决CPU的忙碌程度。

使用场景:从原理上可以看出,CADisplayLink适合做界面的不停重绘,比如视频播放的时候需要不停地获取下一帧用于界面渲染。

+ (CADisplayLink*)displayLinkWithTarget:(id)targetselector:(SEL)sel

1

参数:

- target: 调用者

- sel: 执行的方法

示例:

- (void) displayLink {    CADisplayLink *display = [CADisplayLink displayLinkWithTarget:selfselector:@selector(displayRun:)];// 大概1s执行一次display.preferredFramesPerSecond=2;    [display addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];}- (void)displayRun:(CADisplayLink *)link {staticNSIntegernum =0;NSLog(@"%ld", (long)num);    num++;if(num >4) {        [link invalidate];NSLog(@"end");    }}


这里的示例不太恰当, 不应该在这种场合使用,

另外, 我们可以使用他的paused属性, 来使其暂停, 或继续:

// 暂停display.paused=YES;// 继续display.paused=NO;



文章来自:http://blog.csdn.net/lqq200912408/article/details/53959222

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,050评论 4 370
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,538评论 1 306
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 111,673评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,622评论 0 218
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,047评论 3 295
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,974评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,129评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,893评论 0 209
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,654评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,828评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,297评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,619评论 3 262
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,326评论 3 243
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,176评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,975评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,118评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,909评论 2 278

推荐阅读更多精彩内容