runloop的底层分析

runloop定义

  • runloop:运行循环

不管是我们的操作系统,还是我们的程序,他难道是运行起来之后,泡完一堆代码就停了吗,肯定不是的,他们都是运行玩一堆代码之后,即使没事干了,他要是在运行着呢,等待有事件过来触发,他在继续运行,这就是runloop
在我们的iOS程序中有一个main函数:


image.png

第一:这个main函数是执行完就退出了吗,当然不是。
第二:以前有没有听老师说过这个main函数是个死循环吗,这个函数里的 UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]))会开启一个死循环,而这个死循环就是我们的runloop

  • runloop的作用

    1. 保证程序不退出。他就是一个死循环
    2. 负责监听事件,触摸,时钟,网络事件(系统事件,内核事件等等)
    3. 如果没有事情,我就睡觉
  • runloop逐步分析学习

image.png

由上图代码可知:
我们这个定时器的方法是在主线程上跑的,那么我们想个问题,如果此时我们有何scrollview,我们不停的在拖拽住这个scrollview,那么这个定时器方法还会走吗?
答案:肯定是不会走的,为什么呢?首先我们会说主线程阻塞了,主线程在干UI的事,所以时钟事件就被阻塞了,这个也对,但是今天我们从runloop的角度来分析一下这个问题:
我们学习runloop相信大家都见到过苹果api文档里面的这个图:


image.png

这个图呢,也就是苹果官方文档告诉我们有上图这三种事情是有runloop监听的(Observer, Source, Timer)
至此:我们发现在“其中一种”模式下,runloop可以监听三种事情,但是要知道某一时刻他只能处理这个模式一下的一种事件。

继续引入

上面我们提到了说在其中一种模式下这个模式的概念,那么runloop一共有几种模式呢,答案是5种模式(我们需要掌握的是2种即可)即--UITraking和Default模式


image.png

现在我们就分析一下上面的timer方法不走的问题:
1,拖拽scrollview也就是触摸事件,他最终就是Source事件
2,我们的定时器就是Timer,它是timer事件
3,我们要知道scrollview的触摸事件是在UITraking模式的,即它是UITraking模式下的 Source事件
4,我们还要知道NSTimer的定时器默认是加在Default模式下的,即它是Default模式下的Timer事件
好,有了这四个常识之后,我们来说一下runloop处理上面问题的过程:

  • 首先上去没事干的时候runloop是在一边睡觉的(当然会有一些系统事件需要他处理,不过会非常快,这里我们就不考虑了,不影响我们分析问题,只是让你知道他并不是完全的在睡觉)
  • 接下来我们不是在有了一个定时器吗,它是在Default模式下的timer事件,这时候由于定时器是一秒一次,所以我们的runloop就是一秒跑到default模式下处理一下他的timer事件(当然如果default模式下有observer或者source事件他也处理,就是跑到default模式下处理他的事件),处理完之后,啪,runloop就走了,就去睡觉了,所以我们只弄定时器的时候发现打印很及时,就是因为他一秒来一次,然后睡觉,在一秒又过来处理,完了再去睡觉,很及时,所以我们发现都是再正常打印
  • 然后我们开始不断的拖拽触摸这个scrollview,因为这个是在UITraking模式下的source事件,这时候,runloop的过程就是:由于我们是拖拽这个scrollview,所以这个UITraking模式下的source事件就不停的要走,所以runloop就是来处理UITraking模式下的source事件,处理完刚走立马又被叫回来处理,特别快,不停的处理睡觉,处理睡觉,完全顾不上去处理Default模式下的timer事件,所以就造成了我们上面问题中的定时器方法不走。(你会疑问,我草不是定时器先开始的吗,不应该是先处理我们的定时器吗,即使不是,也应该处理一段sources事件之后,我们定时器需要了他应该过来一次再去处理UITraking模式下的source事件啊,怎么完全就不过来了呢,这里牵涉到一个基本优先级问题,就是如果UITraking模式和Default模式都有事件需要runloop处理,那么他会优先处理UITraking模式模式下的事件)。
  • 到这里就通过runloop说明了为什么定时器方法不走了

继续引入

由于上面那样做的话定时器不会及时走,但是我们还是想要让定时器的方法在拖拽scrollview的时候能够继续走,我们怎么办呢?
答案:就是把这个定时器加到UITraking模式下,这样他就在和scrollview的触摸事件source在同一个模式下了


image.png

这样就把定时器加到了UITraking模式下,这时候你去拖拽scrollview的时候这个定时器的方法还会走,还会打印。
但是没完,你会发现,卧槽,当我松开不去拖拽scrollview的时候,这个定时器也不走了,这是什么情况:

  • 这是因为,这个UITraking模式是在有UI交互的事件触发的时候,才会唤醒runloop过来处理,而现在我们不动scrollview了,runloop就去睡觉了,所以UITraking模式下的timer事件也就不会执行了(UITraking 模式,只有UI交互的时候才会去唤醒runloop过来处理,所以这个定时的事件不能够唤醒runloop过来处理)。
  • 所以稍微总结一下,这个定时器默认的都是default模式,所以runloop就是,当有UI交互事件的时候,他会立马被叫到UITraking模式这里来处理(因为优先级比default模式高),如果没有UI交互事件他就去休息了,如果有default模式下的事件的时候,他就被叫到default模式下了(除了我们所说的UI交互事件,其他的默认的事件一般都是在default模式下的,当然我们可以像上面的定时器一样来修改她所在的模式)

上面说了那么多,问题依然没被完全解决,现在不我们停止了用户交互,runloop就离开了UITraking模式,所以定时器也不被执行了,我们想要的是拖拽的时候不影响执行定时器,不拖拽还不影响,那么我们思考,这该怎么办呢,我们想有没有一种模式是可以把这个定时器既添加到UITraking模式也添加到default模式呢,答案是有,就是下面写的runloop模式的第三种模式NSRunLoopCommonModes


image.png

到这里OK了,问题彻底被解决了,定时器可以欢快的跑起来了

runloop模式

  • NSDefaultRunLoopMode--默认模式
  • UITrackingRunLoopMode--用户交互模式(只要用用户交互事件!!runloop就会切换到这个模式下)
  • NSRunLoopCommonModes--占位模式!占有了上面两种模式。上面两种模式下都有效(上面两种是特定模式,这个并不是特定模式,只是上面两种都有效果,所以我们最初讲的就没说有三种模式而是说有两种模式)
  • 4.5两种模式不要管,系统内核事件模式,项目初始化模式,这两种模式我们根本无法处理和触摸得到,我们根本管不了,所以我们不要管这两种模式

线程和runloop的几个概念

  1. 每一条线程上都有一个runloop
  2. 主线程为什么不会挂掉???因为主线程的runloop正在run
  3. 子线程上面的runloop默认不启动,所以我们平时用的子线程执行完代码就挂掉了

分析1,我们每开辟一个子线程都会有一个对应的runloop,只是这个runloop默认没有开启
分析2,主线程的runloop默认是开启的,并且主线程为什么一直不会挂掉呢,就是我们开头说的main函数里面是个死循环,一直保持runloop在run,所以runloop在run所以主线程就不会挂掉
分析3,子线程平时我们通过gcd开辟的子线程使用完之后不就销毁了,这就是因为runloop默认不启动。你会疑问,觉得gcd的确是自己销毁了,但是我们平时学的NSThread这种我来个全局对象,这个字线程执行完代码,self.thread对象不是还在吗,没有挂掉啊,这个你怎么解释,其实这个并不是线程没有挂掉,而是你的oc线程对象没有销毁,其实内部的线程已经没有了,你可以在此时再次试图通过self.thread去start一下,就会崩了,这就说明,只是你的oc对象还在,他对应的里面的线程已经被销毁了

引入,通过上面的学习,你觉得我们上面解决定时器的那个问题,有问题吗?

答案是有的,为什么呢,你想啊我们刚才不管是通过打印还是学到的知识都知道当前的定时器是添加在主线程的runloop上的,这个没疑问吧,现在问题来了:
1,如果我在定时器方法里面做耗时操作怎么办(比如说我让当前线程(主线程)睡眠5秒中),这时候我们拖scrollview是不是就会特别卡,当然我们学习oc第一天老师就告诉我们不能在主线程做耗时操作,但是目前我的需求就是这样要在定时器方法里面做耗时操作,这时我们应该怎么办?
2,怎么办,当然是把这个定时器放到子线程了,接下来看截图中的代码


image.png

这样是不是就可以了,肯定不行的,我们上面说了,子线程执行完就销毁了,还怎么去执行定时器方法啊,那么为什么会销毁,上面也说了,因为默认子线程的runloop没有开启,所以我们需要把子线程的runloop开启


image.png

开启完之后,这子线程就不会销毁了,所以定时器的方法就可以愉快的在子线程的runloop处理下运行了,这时候即使在定时器的方法中做耗时操作也丝毫不影响我们拖拽scrollview的卡顿问题

提问:如果我们现在在runloop的run之后紧挨着写一个打印,他会执行吗?


image.png

答案肯定是不会的,因为[[NSRunLoop currentRunLoop] run];一运行就相当于死循环了,这个线程下面的代码就不会走了,所以肯定就不会打印了,这个和主线程的runloop类似,也就是和咱们上去讲的main函数里的一样,死循环了

你会疑问,你都死循环了,我程序怎么还会走,你想啊,人家这个字线程死循环管你程序什么事,只是我这个线程里面死循环了,你外面该怎么着还怎么着就行了

继续引入
卧槽,尽管咱们说没关系,不影响程序跑,但是在子线程中死循环并且永远也停不了,不可控制,这总归不好吧,所以我们要自己实现这个runloop,他不就是个死循环吗,我们自己也来模拟这个死循环,并且用过变量来控制是否结束死循环这不就完美了吗?
看下面代码:


image.png

到此终于结束了,我们完美解决了问题

还有啊,咱们这个完美觉了定时器被阻塞,或者定时器耗时操作卡顿住UI,但是你没发现有点小麻烦啊,你会说,我平时都不用NSTimer定时器我用的都是gcd的那种:


image.png

谁还用你说的这个NSTimer,在此说明一点这个gcd的内部封装其实就是咱们上面自己写的,gcd只是做了封装而已,仅此而已

写在最后

上面我们说了给timer定时器加到了子线程里,但是子线程执行完就销毁了。

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

推荐阅读更多精彩内容