NSTimer,即计时器,用于定时执行一些任务,一次或者多次。

系统Foundation框架提供的最常用方法如下,创建一个NSTimer,并将它放到当前runloop的default mode中。

+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti // 执行任务的时间间隔
                      target:(id)aTarget // 执行任务的对象
                     selector:(SEL)aSelector // 执行任务的对象方法
                     userInfo:(nullable id)userInfo // 用于传一些参数
                     repeats:(BOOL)yesOrNo; // 是否重复执行

1、怎么保证在未来某个时间点,要执行任务时,target还有效呢?target完全有可能被释放了呀。鉴于此,NSTimer会持有target对象,直到NSTimer invalidate。不同的是,一次性NSTimer会在执行完任务之后,会自动invalidate;而重复性NSTimer,则需要手动invalidate,否则会造成内存泄漏。

2、由于NSTimer会持有target对象,如果刚好target对象就是self,而NSTimer又是self的一个实例变量,就会引发循环引用:self->NSTimer->self。

@interface TimerTest () {
NSTimer *_timer; // self持有_timer
} @end @implementation TimerTest - (id)init
{
if (self = [super init]) {
_timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(test) userInfo:nil repeats:YES]; // _timer持有self
}
return self;
} - (void)dealloc
{
// 因为_timer持有self,所以self的引用计数>=1,也就执行不到dealloc。
[_timer invalidate]; // 而执行不到dealloc,这句也就执行不到,_timer会一直持有self。这时候只能其他地方将_timer invalidate才行。
} - (void)test
{ } @end

为了解决这种循环引用问题,主要有两种方法:

1)实现一个NSTimer的category,并提供block接口。

@implementation NSTimer (Safe)

+ (NSTimer *)safeScheduledTimerWithTimeInterval:(NSTimeInterval)interval
repeats:(BOOL)repeats
blk:(void(^)())blk {
return [self scheduledTimerWithTimeInterval:interval
target:self // target变成了NSTimer类对象,类对象由系统自动回收
selector:@selector(_doBlock:) // NSTimer会把自己传过去
userInfo:[blk copy] // 把block拷贝到堆上,避免栈block到用时已经被回收了
repeats:repeats];
} + (void)_doBlock:(NSTimer *)timer {
void(^blk)() = timer.userInfo;
if(blk) {
blk();
}
} @end @interface TimerTest () {
NSTimer *_timer; // self持有_timer
} @end @implementation TimerTest - (id)init
{
if (self = [super init]) {
__weak TimerTest *weakSelf = self;
_timer = [NSTimer safeScheduledTimerWithTimeInterval: repeats:YES blk:^{
__strong TimerTest *strongSelf = weakSelf;
[strongSelf test];
}];
}
return self;
} - (void)dealloc
{
[_timer invalidate];
} - (void)test
{ } @end 

3、NSTimer必须放在runloop中才能生效。如下方法就只是创建一个NSTimer,而并没有把它放到runloop中。

+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti
target:(id)aTarget
selector:(SEL)aSelector
userInfo:(nullable id)userInfo
repeats:(BOOL)yesOrNo;

需要调用如下方法把它放到runloop中,才能生效。

- (void)addTimer:(NSTimer *)timer forMode:(NSRunLoopMode)mode;

https://developer.apple.com/documentation/foundation/nstimer/2091889-scheduledtimerwithtimeinterval

NSTimer深入理解的更多相关文章

  1. iOS开发中深入理解CADisplayLink和NSTimer

    一.什么是CADisplayLink 简单地说,它就是一个定时器,每隔几毫秒刷新一次屏幕. CADisplayLink是一个能让我们以和屏幕刷新率相同的频率将内容画到屏幕上的定时器.我们在应用中创建一 ...

  2. 关于NSRunLoop和NSTimer的深入理解

    一.什么是NSRunLoop NSRunLoop是消息机制的处理模式 NSRunLoop的作用在于有事情做的时候使的当前NSRunLoop的线程工作,没有事情做让当前NSRunLoop的线程休眠 NS ...

  3. 深入理解CADisplayLink和NSTimer

    一.什么是CADisplayLink 简单地说,它就是一个定时器,每隔几毫秒刷新一次屏幕. CADisplayLink是一个能让我们以和屏幕刷新率相同的频率将内容画到屏幕上的定时器.我们在应用中创建一 ...

  4. 深入理解RunLoop

    网上看的一篇文章,写的真好,我得多看几次好好理解理解 膜拜大神,转载至此便于学习查看. 此处标明原文链接:http://blog.ibireme.com/2015/05/18/runloop/    ...

  5. iOS开发——高级篇——iOS 中的 NSTimer

    以前的老代码在使用 NSTimer 时出现了内存泄露 NSTimer fire 我们先用 NSTimer 来做个简单的计时器,每隔5秒钟在控制台输出 Fire .比较想当然的做法是这样的: 1 2 3 ...

  6. __block 与 __weak的区别理解

    Blocks理解: Blocks可以访问局部变量,但是不能修改 如果修改局部变量,需要加__block __block int multiplier = 7; int (^myBlock)(int) ...

  7. Swift: 深入理解Core Animation(一)

    如果想在底层做一些改变,想实现一些特别的动画,这时除了学习Core Animation之外,别无选择. 最近在看<iOS Core Animation:Advanced Techniques&g ...

  8. RunLoop机制理解

    一.浅识RunLoop RunLoop在开发中我们一直在用,但是没有注意他.要想理解RunLoop,首先我们需要先了解一下程序运行机制. 程序运行机制:我们都知道OC是运行时语言,也就是说对象的类型是 ...

  9. IOS开发中NSRunloop跟NSTimer的问题

    在Windows时代,大家肯定对SendMessage,PostMessage,GetMessage有所了解,这些都是windows中的消息处理函数,那对应在ios中是什么呢,其实就是NSRunloo ...

随机推荐

  1. Matlab Euler's method

    % matlab script to test efficiency of % Euler's method, classical Runge-Kutta, and ode45 % on Arenst ...

  2. 26个精选的JavaScript面试问题

    译者按: 从各个平台精选整理出26道由浅入深的题目助你面试 原文: Top 26 JavaScript Interview Questions I Wish I Knew 译者: Fundebug 为 ...

  3. (二)在实战中使用Sass和Compass

    第三章 无需计算玩转CSS网格布局 3.1 网格布局介绍 3.2 使用网格布局 3.2.1 术语 术语名 定义 是否涉及HTML标签 列 内容度量的垂直单位 否 容器 构成一个网格布局的HTML元素 ...

  4. 关于z-index使用方法

    z-index控制的是元素的层叠顺序,当z-index越大此层越靠上:但是z-index需在已给元素定位(定位方式不限)的前提下否则该属性失效!! jquery获取index值的方法: $(" ...

  5. 每篇半小时1天入门MongoDB——1. MongoDB介绍和安装

    目录:ASP.NET MVC企业级实战目录 MongoDB简介 MongoDB是一个高性能,开源,无模式的文档型数据库,是当前NoSql数据库中比较热门的一种.它在许多场景下可用于替代传统的关系型数据 ...

  6. Oracle 中 流程控制函数 decode用法

    函数介绍 DECODE函数是ORACLE PL/SQL的功能强大的函数之一,目前还只有ORACLE公司的SQL提供了此函数,其它数据库厂商的SQL实现还没有此功能. DECODE有什么用途呢?先构造一 ...

  7. loadrunner 场景设计-设置结果文件保存路径

    场景设计-设置结果文件保存路径 by:授客 QQ:1033553122 Results->Results settings Results Name 结果文件夹名称 Directory 指定结果 ...

  8. linux上用newman+postman进行自动化测试

    第一步:导出postman文件 Postman就是根据collection和enviroment这两个json文件来自动化运行的! 所以从Postman中导出collection和enviroment ...

  9. 【HANA系列】SAP HANA XS使用Data Services查询CDS实体【一】

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[HANA系列]SAP HANA XS使用Dat ...

  10. 使用iTextSharp导出PDF

    /// <summary> /// 导出至PDF /// </summary> /// <param name="dt">数据源</par ...