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. Hash Table (youtube)

    here is a link for youtube about hash table which is super good https://www.youtube.com/watch?v=h2d9 ...

  2. 设计模式之命令模式(Command )

    命令模式是我们能够实现发送者和接收者之间的完全解耦,发送者是调用操作的对象,而接收者是接收请求并执行特定操作的对象.通过解耦,发送者无需了解接收者的接口.在这里,请求的含义是需要被执行的命令. 作用 ...

  3. 通过写一个Demo展示C#中多种常用的集合排序方法

    不多说,程序很简单,就是将集合中的数据进行排序,但使用到的知识点还是比较多的,大牛勿喷,谨献给初学者!直接上程序吧! namespace Demo { /// <summary> /// ...

  4. Raspberrypi 3B+ 安装 php+sqlite

    按照网上的命令都为安装php5-fpm 和 php5-sqlite, 但是发现无法找到软件,可能是系统版本比较高的缘故,原来的版本已经不支持了. 经过努力华找到如下安装方法 sudo apt-get ...

  5. 原生js 对象深拷贝

    经常需要copy一个对象,又怕拷贝有问题,那下面这段就很方便啦,不用担心copy只是一个引用了. /** @ values 需要copy的变量 */ function deepClone(values ...

  6. 【读书笔记】iOS-xib,自动布局(二)

    我们紧接着上面的文章来写. 一,此时没有选择自动布局的时候 . 二,选中Use Auto Layout.如下图所示. 三,自动布局与不自动布局最明显的区别是右下角多了4个按钮.如图所示. 四,设置On ...

  7. React 入门学习笔记整理(一)——搭建环境

    使用create-react-app脚手架搭建环境 1.安装node .软件下载地址:https://nodejs.org/en/,我下的推荐的版本. 安装之后测试是否安装成功.windows系统下, ...

  8. java Name [jdbc/myjavadb] is not bound in this Context. Unable to find [jdbc].

    一.出错时的情况: 首先,这是一个servlet项目 1.项目的web.xml配置了:(后来发现不配置这个也行,但是tomcat一定要配置) <resource-ref> <desc ...

  9. WebGL学习笔记(一)

    作者:朱金灿 来源:http://blog.csdn.net/clever101 (一)WebGL是什么? WebGL是一门在网页上显示三维图形的技术,你可以把它理解为把OpenGL从C/S端搬到了B ...

  10. Ado.net 访问Oracle乱码问题

    之前安装Oracle - OraClient10g_home1客户端,用Ado.net访问没有问题,后来安装ODP之后,自动的又安装了Oracle - OraClient12Home1,之后就出现乱码 ...