iOS RunLoop(一)

级别: ★★☆☆☆
标签:「iOS」「RunLoop」「线程常驻」
做者: 陈彬
审校: QiShare团队php

前言:
这篇文章主要内容是介绍RunLoop的一些概念以及用法:使用RunLoop建立常驻线程、自定义输入源进行线程通讯等。同时借此机会但愿可以和你们一块儿讨论RunLoop相关的知识,加深对RunLoop的理解。git

1、RunLoop是什么?

RunLoop是与线程相关的基础架构中的一部分,它是一个处理事件的循环(线程进入这个循环,运行事件处理程序来响应传入的事件),RunLoop的目的是当有事件须要处理时,线程是活跃的、忙碌的,当没有事件后,线程进入休眠。github

RunLoop结构以及事件来源:微信

一个RunLoop包含若干个Mode,每一个Mode包含若干个Source/Timer/Observer/Port。当启动一个RunLoop时会先指定一个Mode,检查指定Mode是否存在以及Mode中是否含有SourceTimer,若是Mode不存在或者Mode中无SourceTimer,认为该Mode是一个空的ModeRunLoop就直接退出。架构

Input Source:
  • Port-Based Sources:监听AppMach Port,由内核发出信号,输入源收到信号后,执行相关的例程。oop

  • Custom Input Sources:监听自定义的输入源,须要在其它线程手动发送信号,输入源收到信号后,执行相关的例程。学习

  • Cocoa Perform Selector Sources:Cocoa中自定义的输入源,目的是在不一样线程中执行任务,同一线程中的任务是顺序执行的,当任务执行完成后系统会自动移除这个源。(注意:在目标线程中执行任务时,这个目标线程必须有活跃的RunLoop线程

Timer Source:

时间源会在预设的时间同步传递事件给对应的线程,计时器是线程通知本身作某事的一种方式。翻译

计时器并非真正的实时的,当计时器未处于RunLoop当前监听的Mode,那么计时器是不会计时调度任务的,只有RunLoop当前监听的Mode是计时器关联的Mode时,计时器才会开始执行任务,例如:NSTimer添加至主线程RunLoopDefaultMode中,此时滑动TableView/ScrollView时,RunLoop会切换至TrackMode,计时器是不会调度任务的。3d

若是RunLoop在执行一个例程时,计时器触发了,那么计时器会等待RunLoop将该例程执行完成,在下一次的循环中处理。在RunLoop未运行状况下,计时器永远不会触发任务。

2、RunLoop怎么使用?

应用启动时,会自动在主线程上设置运行RunLoop,因此不须要在主线程上显示的启动RunLoop,无需调用[[NSRunLoop currentRunLoop] runUntilDate:]这些方法。那么若是咱们显示的在主线程中调用RunLooprun方法会出现什么结果呢?经过Demo中显示,主线程中显示启动RunLoop会影响当前事件处理,可是因为RunLoop并无中止,因此其余事件可以正常接收和处理。

而子线程也不并是必需要设置运行RunLoop才能执行任务,好比说只是简单在子线程中处理个耗时任务等,以下场景是须要启动RunLoop的:

  1. 使用NSPort或者自定义输入源与其它线程通讯。
  2. 在线程上使用计时器。
  3. 在一个Cocoa应用中使用performSelector相关方法。
  4. 使线程常驻,在该线程按期执行任务。

正如前言中所说,本文主要说明线程常驻和自定义输入源线程通讯。

线程常驻:

方式一:无条件的启动RunLoop是最简单的选择,但它也是最不可取的选择,它会将线程置于永久循环中,这样几乎没法控制RunLoop自己,虽然能够添加和删除输入源和计时器,但中止RunLoop的惟一方法是杀死RunLoop。(以上内容是经过Google翻译的官网内容可能理解有些误差届时还望指正,事实上我在作实验的过程当中,发现使用NSThreadcancel方法是没法中止RunLoop的,cancel方法是更改线程的取消状态,指示它应该退出。在当前线程下执行[NSThread exit]方法,退出了该线程,但demo中的LongLifeThreadViewController仍然未被释放)

方式二:启动RunLoop时设定时限,RunLoop将一直运行直到事件到达或分配的时间到期。若是事件到达,则将该事件分派给处理程序进行处理,而后退出这次RunLoop。能够经过从新启动RunLoop处理下一个事件。一样若是分配的时间到期,也能够从新启动RunLoop来处理。这种方式能够指定RunLoopMode,官网力荐。

自定义输入源线程通讯:

定义输入源:

  1. 提供输入源要处理的信息。
  2. 接收到事件时的执行例程。
  3. 输入源加到RunLoop时的执行例程。
  4. 输入源失效时的执行例程。

我的感受能够根据我的需求决定是否实现第三、4两条内容。(注意定义输入源只能经过CoreFoundation提供的对应API实现,其中的回调例程由C语言实现)

RunLoop上安装输入源:若是实现了上述的第3条内容时,将自定义的输入源添加到RunLoop时,就会回调输入源对应的schedule实现例程。

向输入源发送信号:输入源在接收到信号后,会执行对应的perform例程,perform例程就是对应事件处理程序。(注意若是线程处于休眠状态,要唤醒线程,不然该事件没法被处理。


结语:
关于RunLoop的内容还有不少,好比:RunLoopModesRunLoopObserverNSPortNSTimer等等,固然还有RunLoop的源码,这些内容在此并未列出,若有感兴趣的小伙伴能够先行花时间去探索、学习,到时能够一块儿交流、讨论。

源码地址:QiRunLoopDemo1


小编微信:可加并拉入《QiShare技术交流群》。

关注咱们的途径有:
QiShare(简书)
QiShare(掘金)
QiShare(知乎)
QiShare(GitHub)
QiShare(CocoaChina)
QiShare(StackOverflow)
QiShare(微信公众号)

推荐文章:
iOS 经常使用调试方法:LLDB命令
iOS 经常使用调试方法:断点
iOS 经常使用调试方法:静态分析
iOS消息转发
iOS 自定义拖拽式控件:QiDragView
iOS 自定义卡片式控件:QiCardView
iOS Wireshark抓包
iOS Charles抓包
奇舞周刊

相关文章
相关标签/搜索