初次接触 RunLoop

最近看了两位大神关于RunLoop的一些资料,对RunLoop算是有了一个初步的认识,在这里整理总结一下自己对于RunLoop的理解。如果我有理解错的地方,希望大家帮我指出。我看的是 @ibireme 的博客和 @我就叫Sunny怎么了 的视频,链接在下面。

深入理解RunLoop

iOS线下分享《RunLoop》by 孙源@sunnyxx

什么是RunLoop


说白了就是一个循环,能够让整个程序一直运行着不会退出,接收用户的信息。当然这个循环是一个十分复杂的循环。

RunLoop与线程的关系


每个线程有一个自己的main函数,当这个main函数执行完后这个线程也就没了。所以我们需要给这个线程创建一个 RunLoop。但是在线程创建的时候并没有 RunLoop ,只有当我们第一次去获取这个线程的 RunLoop 时,才会创建。

RunLoop的结构


每个 RunLoop 中有一个集合其中存放了若干个 Mode,一个 RunLoop 只能在一种 Mode 下运行,如果需要切换那么久要退出 Loop 再重新指定一个 Mode 进入。每个 Mode 中又有若干个 item ,总共有三类,分别是 Source,Timer,Observer。先说简单的

Timer

Timer 就是一个定时器,我们平时使用 NSTimer 与它紧密相关。它包含一个时间长度和一个作为回调的函数指针,当它加入到 RunLoop 后,每次到那个时间点,就会执行一次回调。

Observer

Observer 就是观察者,它也包含一个回调,当 RunLoop 的状态改变时,就会执行这个回调。RunLoop 有以下几种状态:

typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry         = (1UL << 0), 
    kCFRunLoopBeforeTimers  = (1UL << 1),
    kCFRunLoopBeforeSources = (1UL << 2), 
    kCFRunLoopBeforeWaiting = (1UL << 5), 
    kCFRunLoopAfterWaiting  = (1UL << 6), 
    kCFRunLoopExit          = (1UL << 7), 
};

Source

Source就比较难理解了。反正我自己没有弄明白,还是引用一下@ibireme的原话吧。

CFRunLoopSourceRef 是事件产生的地方。Source有两个版本:Source0 和 Source1。

Source0 只包含了一个回调(函数指针),它并不能主动触发事件。使用时,你需要先调用 CFRunLoopSourceSignal(source),将这个 Source 标记为待处理,然后手动调用 CFRunLoopWakeUp(runloop) 来唤醒 RunLoop,让其处理这个事件。

Source1 包含了一个 mach_port 和一个回调(函数指针),被用于通过内核和其他线程相互发送消息。这种 Source 能主动唤醒 RunLoop 的线程

CFRunLoopMode 和 CFRunLoop 的结构

我觉得看了下面这两个结构体后,对于 RunLoop 的理解会明朗很多。

struct __CFRunLoopMode {
    CFStringRef _name;            // Mode Name
    CFMutableSetRef _sources0;    // Set
    CFMutableSetRef _sources1;    // Set
    CFMutableArrayRef _observers; // Array
    CFMutableArrayRef _timers;    // Array
    ...
};
 
struct __CFRunLoop {
    CFMutableSetRef _commonModes;     // Set
    CFMutableSetRef _commonModeItems; // Set<Source/Observer/Timer>
    CFRunLoopModeRef _currentMode;    // Current Runloop Mode
    CFMutableSetRef _modes;           // Set
    ...
};

__CFRunLoop 中有一个 _modes 用来存放这个 RunLoop 中的所有 Mode。还有一个 _currentMode 用来标记当前的 Mode。而在 _CFRunLoopMode 中除了 _name 就是那三种 item,应该很容易看出来。

需要一提的是在 __CGRunLoop 中有 CFMutableSetRef _commonModes; CFMutableSetRef _commonModeItems; 这个两个结构。我们可以把一个 Mode 标记为 Common。 这样每次 RunLoop 内容改变时都会把 _commonModeItems 里的 items 全部同步到标记了 Common 的 Mode 中。在后面讲 timer 的例子时我们还会再提到。

RunLoop 的内部逻辑

还是引用一下@ibireme的图片,十分的直观明了。

RunLoop 应用


NSTimer

在我刚刚学 iOS 的时候,一次偶然知道了 NSTimer 这个东西后,高兴的用它模仿了一个系统的计时器。源码在一次整理的时候被我删了。。只能看看截图了:

本来挺开心的,结果一滚动下面的表格,WHAT?!计时器怎么不动了??

后来也查了解决办法,不过一直没搞懂为什么。现在知道了 RunLoop 后总算是明白了。

在主线程中有预置两个 Mode,一个是 Default,还有一个就是 UITrackingRunLoopMode,并且这两个Mode都标记了 Common。这个 Mode 在 ScrollView 滚动时会切换,用来保证滚动的流畅。而我们注册 timer 使用的方法:

 _timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];

会默认把这个 timer 注册到当前 RunLoop 的 DefaultMode 中,那么滚动的时候 Mode 一切换 timer 很显然就没法工作了。解决办法也很容易,可以分为两种:

  • 把timer注册到UITrackingRunLoopMode或者NSRunLoopCommonModes(添加到 CommonModes 中就相当于自动添加到了UITrackingRunLoopMode)。
// 二选一
[[NSRunLoop currentRunLoop] addTimer:_timer forMode:UITrackingRunLoopMode];
[[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
  • 把timer添加到子线程的 RunLoop 中,不管是什么Mode都不会影响到主线程
dispatch_queue_t queue = dispatch_queue_create("com.cyrusdev.queue", DISPATCH_QUEUE_CONCURRENT);
   dispatch_async(queue, ^{
       _timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
       [[NSRunLoop currentRunLoop] run]; // 一定要在注册timer之后让 RunLoop 跑起来
   });

我写了一个Demo,地址在这里,可以感受一下:

RunLoopDemo

AutoreleasePool

在 App 启动后,系统会在主线程的 RunLoop 里注册两个 Observer。

第一个 Observer 监听 kCFRunLoopEntry 也就是即将进入 RunLoop 的状态,在这里会创建一个自动释放池。

第二个 Observer 监听 kCFRunLoopBeforeWaiting 以及 kCFRunLoopExit 两个状态。在 RunLoop 即将进入休眠时释放旧池,创建新池。在 RunLoop 要退出时释放池子。

让一个子线程一直运行

- (void)viewDidLoad {
    [super viewDidLoad];
    // 创建子线程
    self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadMain) object:nil];
    [self.thread start];
}

- (void)threadMain {
    [[NSThread currentThread] setName:@"myThread"]; 
    NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
    [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
    [runLoop run];
}

我在 myThread 线程中创建了一个 RunLoop,这个线程就不会自动销毁了。我们可以暂停程序看一下它的线程堆栈:

总结


对于 RunLoop 的了解其实还很浅,只能有一个大概的结构,不过了解了这些内容对于整个App运行的过程还是会更加深入一些了。

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

推荐阅读更多精彩内容

  • 原文地址:http://blog.ibireme.com/2015/05/18/runloop/ RunLoop ...
    大饼炒鸡蛋阅读 1,145评论 0 6
  • 转载:http://www.cocoachina.com/ios/20150601/11970.html RunL...
    Gatling阅读 1,434评论 0 13
  • 转自http://blog.ibireme.com/2015/05/18/runloop 深入理解RunLoop ...
    飘金阅读 972评论 0 4
  • 深入理解RunLoop 由ibireme| 2015-05-18 |iOS,技术 RunLoop 是 iOS 和 ...
    橙娃阅读 847评论 1 2
  • 真正的风,土,人,情体验 毫不含糊地过了个70年代的夏天 恍如作了一场深刻的梦 洗澡时经常会断水断电 在好心情加好...
    哇子阅读 413评论 0 0