RunLoop问题集

什么是RunLoop?

答:RunLoop是线程相关的基础框架中的一部分,是一个事件处理对象,每一个线程都有与之对应的RunLoop,但并不是线程创建时就有RunLoop,只有当前线程第一次主动获取RunLoop,系统才会创建当前线程相应的RunLoop。

RunLoop的作用是什么?

答:1).管理线程的生命周期及活动。                                           

     2).处理输入事件源以及通知观察者。

如何使用RunLoop?

答:iOS/OSX提供了Core Foundation(CFRunLoopRef)和Cocoa(NSRunLoop)两套API来使用RunLoop,可以通过CFRunLoopGetMain() 和 CFRunLoopGetCurrent()或[NSRunLoop mainRunLoop]和[NSRunLoop currentRunLoop]来获取RunLoop对象,NSRunLoop是基于CFRunLoopRef的高层组件,CFRunLoopRef的API都是线程安全的,但NSRunLoop的API不是线程安全的。虽然CFRunLoopRef的操作都是线程安全的,但不建议跨线程处理。

如何启动RunLoop?

答:可以通过NSRunLoop对象的run,runUntilDate:,runMode:beforeDate:方法,或通过CFRunLoopRef的CFRunLoopRun(),CFRunLoopRunInMode()来启动RunLoop。

使用NSRunLoop和CFRunLoopRef来启动RunLoop有何不同?

答:虽然NSRunLoop是基于CFRunLoopRef的高层组件,对NSRunLoop的操作最终都会转换成对CFRunLoopRef的操作,但需要注意的是,NSRunLoop与CFRunLoopRef并不是简单的接口转换,就像启动RunLoop一样,NSRunLoop的Run方法与CFRunLoopRun()并不是对应着的,而且有着一定的区别。

NSRunLoop Run
CFRunLoopRun()

如何退出RunLoop?

答:退出RunLoop的方式有三种,分别是:方式一.给RunLoop设定超时时间;方式二.使用CFRunLoopStop()函数显式退出;方式三.移除CurrentMode的所有输入源和定时源。

方式一是官法推荐的方式,可以安全有效地退出RunLoop。方式二,用CFRunLoopStop()函数并不是绝对可以退出Run的,要看以什么方式启动RunLoop,如果用[[NSRunLoop currentRunLoop] run]来启动RunLoop,那么用CFRunLoopStop()是无法退出RunLoop的,正如上面的伪代码所示,以[[NSRunLoop currentRunLoop] run]启动的RunLoop,唯一退出的方式是移除CurrentMode的所有输入源和定时源,也就是方式三,但这种方式是不稳定的,因为系统会添加一些输入源或定时源来完成一些操作。

RunLoop是否自动运行的?

答:所有线程的RunLoop都是默认不启动的,但主线程的RunLoop会随着应用的运行而被启动。

RunLoop处理的输入事件源有哪些?

答:RunLoop处理的事件源有两种,分别是输入源(Input Source)和定时源(Timer Source),而主线程的Main RunLoop还会处理GCD事件源。

什么是RunLoopMode?

答:RunLoop可以有多个RunLoopMode,RunLoopMode包含了输入源(Input Source),定时源(Timer Source)和观察者(Observer)。

RunLoop每次进入Run时都需要指定一个RunLoopMode,指定RunLoopMode后,只有当前RunLoopMode内的源和观察者会被处理,其它的源和观察者需要等到RunLoop运行其RunLoopMode时才会被处理,切换RunLoopMode的唯一方式是,退出当前RunLoopMode,重新指定一个RunLoopMode进入Run。当RunLoop选择一个RunLoopMode进入Run时,若这个RunLoopMode中并没有需要处理的源(输入源或定时源),RunLoop就会直接退出。

RunLoopMode

RunLoopMode中的_timerPort是_timers中所有定时源的公共Port,_portToV1SourceMap记录了_sources1以及对应的Port,通过Port获取相应的Source1。

通过Port获取Source1

什么是CommonMode和CommonModeItem?

答:CommonModeItem是公共ModeItem,CommonMode是公共RunLoopMode,当把一个RunLoopMode注册为CommonMode时,CommonModeItem被会自动添加到CommonMode里,当CommonModeItem有所改动时,CommonMode也会作出相应的改动。

什么是定时源(Timer Source)?

答:用于延时或重复的时间间隔处理事件。

如何使用定时源?

答:CFRunLoopTimerRef是RunLoop中唯一的定时源,以定时器的形式表示和使用,可选择的定时器有,NSTimer,CADisplayLink,CFRunLoopTimerRef,GCD Timer。

四种定时器的区别是什么?

答:NSTimer和CADisplayLink是Cocoa提供的高层定时器,CFRunLoopTimerRef是Core Foundation提供的基础定时器,NSTimer和CADisplayLink是建立在CFRunLoopTimerRef之上的高层组件,而CFRunLoopTimerRef是建立在mk_timer之上。NSTimer和CADisplayLink主要区别在于信号的发射频率不同,CADisplayLink的信号发射频率固定在16.67ms一次,而NSTimer的信号发射频率可自由定义(具体请看iOS10定时消息的改动)。

GCD Timer调用栈

GCD Timer有别于前三种定时器,是由GCD系统所管理的定时器,通过一定的时间间隔dispatch任务到相应的队列中处理,以主线程为例,时间间隔到达后,GCD系统将Block dispatch到主线程对应的Main Queue,等待Main RunLoop检测和处理。

如何选择使用哪一种定时器?

答:除非需要实现定时动画,否则不建议使用CADisplayLink作为定时器(具体请看iOS10定时消息的改动。什么是定时动画?请查看iOS动画的基础知识);NSTimer适用于大部份情况,但需要注意循环引用的问题;GCD Timer的缺点在于,不能在自己所创建的子线程中使用。

CFRunLoopTimerRef的触发原理是怎样的?

答:具体请看iOS10定时消息的改动

什么是输入源?

答:是RunLoop所处理的事件源之一,主要用于线程或进程交互,输入源分为基于端口输入源(Source1)和非端口输入源(Source0)。

基于端口输入源(Source1)与非端口输入源(Source0)的区别是什么?

答:1).Source0与Source1都是CFRunLoopSourceRef类型,但配置方式不同,Source0用CFRunLoopSourceContext来配置,Source1用CFRunLoopSourceContext1来配置。

     2).Source0与Source1都可用于线程(或进程)交互,但交互的形式有所不同,Source1监听端口,当端口有消息到达时,相应的Source1就会被触发回调,完成相应的操作;而Source0并不监听端口,让Source0执行回调需要手动标记Source0为待处理状态,还需要呼醒Source0所在的RunLoop。

Source1交互
Source0交互

      3).从Source0与Source1的交式方式了解到,Source1的交互会主动呼醒所在的RunLoop,而Source0的交互则需要依赖其它线程来呼醒Source0所在的RunLoop。

      4).一次Loop只能执行一个Source1的回调,但一次Loop可以执行多个待处理的Source0的回调。

如何创建Source0?

创建Source0

如何标记Source为待处理状态,且呼醒所在的RunLoop?

Source0交互

如何创建Source1以及如何交互?

答:有Cocoa和Core Foundation两套API来配置和使用Source1;Cocoa有NSPort,NSMachPort,NSMessagePort,NSSocketPort等类,Core Foundation有CFMachPortRef,CFMessagePortRef,CFSocketRef等。其中用得比较多的是NSMachPort和CFMessagePortRef。

CFMessagePortRef
NSMachPort

Cocoa所提供的类只是建立在Core Foundation之上的高层组件,且提供了toll-free bridged。需要注意的是,NSMachPort接收和发送需要是同一个对象;CFMessagePortRef接收和发送的Port所用的name要相同,CFMessagePortSendRequest()函数通过CFMessagePortRef的name来查找相应的接收端口来进行消息发送(不建议直接使用mach_msg()来发送消息,关于Port可以查看Inter-Process Communication)。

RunLoop的内部逻辑是怎样的?

Run内部逻辑
官方文档的Run内部逻辑

需要注意的是第五步,官方文档写的是如果有基于端口的输入源待处理,就进入第九步,这跟CFRunLoop的源码不同。

CFRunLoop Run源码第五步

从源码可以看到,第五步检测的是GCD端口事件,而不是官方文档所写的基于端口的输入源,但经过大量测试发现,第五步实际上会检测所有未处理的端口事件,而并非像官方文档或源码所展示的那样(太坑爹了,居然官方文档跟源码不同,源码又和实际测试结果不同😂)。

如果有什么地方写错的麻烦指出,如果有什么还想知道的请在评论留言,我会尽快补上的。

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

推荐阅读更多精彩内容