iOS开发NSTimer的深入理解

再一次面试中被问到nstimer的争取使用方法,原理,我当时就说了[_timer invalidate],timer =nil;

但是原理没说明,后来经过查找资料查到了,引用原著作者http://www.jianshu.com/p/330d7310339d

发现ViewController的dealloc方法死活没走到, 心里咯噔一下, 不会又内存泄漏了? 😳

一切都是很完美的节奏啊: ViewController初始化时, 创建Sub UIView, 创建数据结构, 创建NSTimer

然后在dealloc里, 释放NSTimer, 然后NSTimer = nil, 哪里会有什么问题?

不对! 移除NSTimer后dealloc就愉快滴走了起来, 难道NSTimer的用法一直都不对?

结果发现, 真的是不对! 😳

好吧, 故事就讲到这里, 马上开始今天的NSTimer之旅吧

创建NSTimer

创建NSTimer的常用方法是

+ (NSTimer*)scheduledTimerWithTimeInterval:(NSTimeInterval)titarget:(id)targetselector:(SEL)aSelectoruserInfo:(id)userInforepeats:(BOOL)repeats

创建NSTimer的不常用方法是

+ (NSTimer*)timerWithTimeInterval:(NSTimeInterval)titarget:(id)targetselector:(SEL)aSelectoruserInfo:(id)userInforepeats:(BOOL)repeats

- (instancetype)initWithFireDate:(NSDate*)date interval:(NSTimeInterval)ti target:(id)target selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)repeats

这几种方法除了创建方式不同(参数), 方法类型不同(类方法, 对象方法), 还有其他不同么?

当然有, 不然Apple没必要这么作, 开这么多接口, 作者(好像就是我😄)也没必要这么作, 写这么长软文

他们的区别很简单:

how-to-user-nstimer-01.png

scheduledTimerWithTimeInterval相比它的小伙伴们不仅仅是创建了NSTimer对象, 还把该对象加入到了当前的runloop中!

等等, runloop是什么鬼? 在此不解释runloop的原理, 但是使用NSTimer你必须要知道的是

NSTimer只有被加入到runloop, 才会生效, 即NSTimer才会被真正执行

所以说, 如果你想使用timerWithTimeInterval或initWithFireDate的话, 需要使用NSRunloop的以下方法将NSTimer加入到runloop中

-(void)addTimer:(NSTimer *)aTimer forMode:(NSString *)mode

how-to-user-nstimer-02.png

销毁NSTimer

知道了如何创建NSTimer后, 我们来说说如何销毁NSTimer, 销毁NSTimer不是很简单的么?

用invalidate方法啊, 好像还有个fire方法, 实在不行直接将NSTimer对象置nil, 这样iOS系统就帮我们销毁了

是的, 曾经的我也是如此混沌滴这么认为着, 那么这几种方法是不是真的都可以销毁NSTimer呢?

invalidate与fire

我们来看看Apple Documentation对这两个方法的权威解释吧

invalidate

Stops the receiver from ever firing again and requests its removal from its run loop

This method is the only way to remove a timer from an NSRunLoop object

fire

Causes the receiver’s message to be sent to its target

If the timer is non-repeating, it is automatically invalidated after firing

理解了上面的几句话, 你就完完全全理解了invalidate和fire的区别了, 下面的示意图更直观

how-to-user-nstimer-03.png

总之, 如果想要销毁NSTimer, 那么确定, 一定以及肯定要调用invalidate方法

invalidate与=nil

就像销毁其他强应用(不用我解释强引用了吧, 否则你还是别浪费时间往下看了)对象一样, 我们是否可以将NSTimer置nil, 而让iOS系统帮我们销毁NSTimer呢?

答案是: 当然不可以! (详见上述的结论, "总之, 巴拉巴拉...")

为什么不可以? 其他强引用对象都可以, 为什么NSTimer对象不可以? 你说不可以就可以? 凭什么信你?

好吧, 我们来看下使用NSTimer时, ARC是怎么工作的

首先, 是创建NSTimer, 加入到runloop后, 除了ViewController之外iOS系统也会强引用NSTimer对象

how-to-user-nstimer-04.png

当调用invalidate方法时, 移除runloop后, iOS系统会解除对NSTimer对象的强引用, 当ViewController销毁时, ViewController和NSTimer就都可以释放了

how-to-user-nstimer-05.png

当将NSTimer对象置nil时, 虽然解除了ViewController对NSTimer的强引用, 但是iOS系统仍然对NSTimer和ViewController存在着强引用关系

神马? iOS系统对NSTimer有强引用很好理解, 对ViewController本来不就是强引用么?

这里所说的iOS系统对ViewController的强引用, 不是指为了实现View显示的强引用, 而是指iOS为了实现NSTimer而对ViewController进行的额外强引用 (我去, 能不能不要这么拗口, 欺负我语文不好)

不瞒您说, 我的语文其实也是一般般, 所以show me the code

NSLog(@"Retain count is %ld",CFGetRetainCount((__bridgeCFTypeRef)self));_timer = [NSTimerscheduledTimerWithTimeInterval:TimerInterval                                          target:selfselector:@selector(timerSelector:)                                        userInfo:nilrepeats:TimerRepeats];NSLog(@"Retain count is %ld",CFGetRetainCount((__bridgeCFTypeRef)self));[_timer invalidate];NSLog(@"Retain count is %ld",CFGetRetainCount((__bridgeCFTypeRef)self));

各位请注意, 创建NSTimer和销毁NSTimer后, ViewController(就是这里的self)引用计数的变化

2016-07-06 13:53:21.950 NSTimerAndDeallocDemo[2028:697020] Retain count is 7

2016-07-06 13:53:21.950 NSTimerAndDeallocDemo[2028:697020] Retain count is 8

2016-07-06 13:53:21.950 NSTimerAndDeallocDemo[2028:697020] Retain count is 7

如果你还是不理解, 那只能用"杀手锏"了, 美图伺候!

how-to-user-nstimer-06.png

关于上图, @JasonHan0991 有不同的解释, 详见评论区, 在此表示感谢!

结论

综上所述, 销毁NSTimer的正确姿势应该是

[_timerinvalidate];// 真正销毁NSTimer对象的地方_timer=nil;// 对象置nil是一种规范和习惯

慢着, 这个结论好像不妥吧?

这都被你发现了! 销毁NSTimer的时机也是至关重要的!

如果将上述销毁NSTimer的代码放到ViewController的dealloc方法里, 你会发现dealloc还是永远不会走的

所以我们要将上述代码放到ViewController的其他生命周期方法里, 例如ViewWillDisappear中

综上所述, 销毁NSTimer的正确姿势应该是 (这句话我怎么看着这么眼熟, 是的, 这次真的结论了)

- (void)viewWillDisappear:(BOOL)animated {    [superviewWillDisappear:animated];    [_timer invalidate];    _timer = nil;}

NSTimer与runloop

上面说到scheduledTimerWithTimeInterval方法时, 有这么一句

schedules it on the current run loop in the default mode

加到runloop这件事就不必再解释了, 而这个default mode应该如何理解呢?

其实我是不想谈runloop的(因为理解不深, 所以怕误导人民群众), 但是这里不得不解释下了

runloop会运行在不同的mode, 简单来说有以下两种mode

NSDefaultRunLoopMode, 默认的mode

UITrackingRunLoopMode, 当处理UI滚动操作时的mode

所以scheduledTimerWithTimeInterval创建的NSTimer在UI滚动时, 是不会被及时触发的, 因为此时NSTimer被加到了default mode

如果想要runloop运行在UITrackingRunLoopMode时, 仍然及时触发NSTimer那应该怎么办呢?

应该使用timerWithTimeInterval或initWithFireDate, 在创建完NSTimer后, 自己加入到指定的runloop mode

[[NSRunLoop currentRunLoop]addTimer:_timerforMode:NSRunLoopCommonModes];

NSRunLoopCommonModes又是什么鬼? 不是说好的只有两种mode么?

是滴, 请注意这里的复数形式modes, 说明它不是一个mode, 它是mode的集合!

通常情况下NSDefaultRunLoopMode和UITrackingRunLoopMode都已经被加入到了common modes集合中, 所以不论runloop运行在哪种mode下, NSTimer都会被及时触发

最后, 我们来做个小测验, 来结束今天的NSTimer讨论吧

测验: 请问下面的NSTimer哪个更准时?

// 1[NSTimerscheduledTimerWithTimeInterval:TimerInterval                                target:selfselector:@selector(timerSelector:)                              userInfo:nilrepeats:TimerRepeats];

// 2[[NSRunLoopcurrentRunLoop] addTimer:_timer                            forMode:NSDefaultRunLoopMode];

// 3[[NSRunLoopcurrentRunLoop] addTimer:_timer                            forMode:NSRunLoopCommonModes];

答案, 第三种是最准确的,不受其他情况影响,第一种和第二种情况相同都是加入到NSDefaultRunLoopMode模式下,如果滑动scrollview的时候,时间计时器会停止,等滑动结束,再继续,所以时间不准确


最后,如果本文章对你有帮助,希望点赞,评论666

本人github开源库地址:https://github.com/lishiping,希望对广大iOS开发者有帮助

Cocoapod公开库:

1.SafeData---使用array,dictionary类别,主要是保护数组防止插入空数组,防止数组越界,在开发中非常实用,字典的类别也非常实用,可以直接得到转换后的类型SafeData

2.SPDebugBar ---A tool to help developers and testers quickly switch the server address, convenient to debug the program.一个小工具帮助开发人员和测试人员快速切换服务器地址,方便调试程序,可以在debug模式下或者测试包上方便切换地址SPDebugBar

3.SPFastPush---Use Macro Fast NavigationController push next VC,and Fast Pop anther VC.使用宏帮助导航控制器快速push下一个Viewcontroller,并使用KVC快速给下一个VC赋值,也可以使用宏定义快速返回上一层VC或者指定的VCSPFastPush

4.SPMacro---一些foundation层的一些宏定义,打印宏定义,类型判断,通知使用,线程使用等,和一些UIKit层的宏定义,屏幕方面的宏定义,颜色方面的宏定义,图片宏定义SPMacro

5.SPWebView---SPWebView是一个基于OC代码实现的WebView轻量级组件,将UIWebView和WKWebView的API封装成统一的类去使用,并且在加载网页的时候提供进度条,同时简化JS与OC互相调用及传递数据的方式。仿微信界面SPWebView

6.SPCategory---SPWebView是一个常用类别库,size,字符串,hud等SPCategory

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

推荐阅读更多精彩内容

  • 一、什么是NSTimer 官方给出解释是“A timer provides a way to perform a ...
    行走的菜谱阅读 1,135评论 0 4
  • 目录 引言 创建NSTimer 销毁NSTimer NSTimer与runloop 附录 引言 为什么想起来要讨论...
    诺之林阅读 16,326评论 25 106
  • 创建NSTimer 创建NSTimer的常用方法是: + (NSTimer *)scheduledTimerWit...
    LanWor阅读 1,355评论 0 2
  • 2017-11-10 我们在三门峡火车站上车。 在站内候车时,遇到了好几个来自平顶山的心理辅导老师,其中有叶县镇中...
    历事炼心阅读 122评论 0 0
  • —— 我叫叶微微,我的家中只有我和我妈俩人,是一个不折不扣的单亲家庭。即使这样我们还要过的很幸福的,即使贫穷—...
    努力创作的小菇凉阅读 259评论 0 1