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. java_有秒计时的数字时钟

    题目内容: 这一周的编程题是需要你在课程所给的时钟程序的基础上修改而成.但是我们并不直接给你时钟程序的代码,请根据视频自己输入时钟程序的Display和Clock类的代码,然后来做这个题目. 我们需要 ...

  2. es6中的Promise学习

    关于Promise Promise实例一旦被创建就会被执行 Promise过程分为两个分支:pending=>resolved和pending=>rejected Promise状态改变后 ...

  3. Java集合之Vector源码分析

    概述 Vector与ArrayLIst类似, 内部同样维护一个数组, Vector是线程安全的. 方法与ArrayList大体一致, 只是加上 synchronized 关键字, 保证线程安全, 下面 ...

  4. Linux环境变量详解与应用

    在bash shell中,环境变量分为: >全局变量 >局部变量 全局变量,不仅对shell可见,对其子进程也可见 查看预设的全局环境变量: ghostwu@dev:~$ printenv ...

  5. 初学HTML-5

    表格标签:用来给一堆数据添加表格语义. 格式:<table> <tr> <td></td> </tr> </table> tab ...

  6. jquery弹窗时禁止body滚动条滚动

    当弹出一个jq提示窗口的时候,一般窗口右边还会有进度条的情况,禁止进度条方法禁止浏览器滚动条滚动: $('body').css({ "overflow-x":"hidde ...

  7. 洛谷P4555 [国家集训队]最长双回文串(manacher 线段树)

    题意 题目链接 Sol 我的做法比较naive..首先manacher预处理出以每个位置为中心的回文串的长度.然后枚举一个中间位置,现在要考虑的就是能覆盖到i - 1的回文串中 中心最靠左的,和能覆盖 ...

  8. ajax文件上传-FormData()

    HTML: <form action=""> <input type="file" id="file1" name=&qu ...

  9. Salesforce 数据库操作简介

    Salesforce 中的数据库操作方式 Salesforce 为用户和开发者提供了四种基本的数据库操作方式: Apex 中的 DML 语句 Apex 中的 Database 类 SOQL 查询 SO ...

  10. Cookie管理 WebView同步

    NoHttp的Cookie管理原理 在文档的初始化配置一章讲了NoHttp如何配置或者禁用cookie自动管理. NoHttp的Cookie自动维护,严格遵守Http协议,即区分临时Cookie和有效 ...