做一个技术方向久了,难免会沉溺其中,对当初开始接触这个方向的许多根本上的疑问渐渐都不了了之,意识上认为然,而不知其所以然。

最近重新梳理iOS的runloop,说说自己的理解,希望能说清楚。

先抛出一个问题:程序入口main函数只是一段代码,为什么只运行了一段代码,程序就可以一直运行?

main函数中起了一个main thread,main thread执行结束,则应用程序退出,为了能让线程一直运行,苹果引入了runloop,苹果这样介绍runloop:

Run loops are part of the fundamental infrastructure associated with threads. A run loop is an event processing loop that you use to schedule work and coordinate the receipt of incoming events. The purpose of a run loop is to keep your thread busy when there is work to do and put your thread to sleep when there is none.

main thread中的runloop一直在跑圈圈(while循环),所以这个main thread不会立即执行结束,如果设定这个runloop跑圈的时间是1年,app就可以运行1年,1年后app自动退出。

这里面有两个问题:

  • runloop如果一直在跑圈,会一直占用/消耗系统资源,怎么避免?
  • 跑圈圈的同时,如果用户有输入等操作,怎么检测?

runloop是基于事件处理的,有事件需要处理,则runlopp开始跑圈检测需要处理的事件,处理结束后,进入休眠,当再次有事件需要处理时,再次被唤醒去跑圈,如此反复。

怎么知道有事件要去处理呢?事件包括许多,计时器、用户输入、点击、网络请求等,main thread中创建main runloop时,会将这些事件作为source加入到runloop中,这样,当其中一个source有变更时,激活runloop去跑圈。当然如果这些runloop中source都被移除了,runloop跑一圈之后就会被释放,线程也随即结束。如果main runloop的source可以全部移除,那就意味着app要退出了,但实际的情况是,main runloop被系统做了特殊处理,是无法真正把source都remove掉的,想通过移除source达到app exit的目的是不行的。

做移动开发的同学经常会被问到iOS系统为什么比Android系统流畅的问题,问题也出在runloop中。runloop中有几种模式,其中一种叫Event tracking模式,意为事件追踪,当用户滚动页面等操作时,runloop会被切换到追踪模式,这个模式会限制后续其他事件,比如在这个模式下,点击、长按等其他事件等都将会被限制,让系统只专注处理当前tracking的事件。整个系统只专注做一件事,当让流畅。

题为runloop之于thread,是说它们之间的关系,thread可以没有runloop,但runloop必须运行在线程中,也就是是apple说的The purpose of a run loop is to keep your thread busy 。thread和runloop是一一对应的,以键值对存储在底层,一个thread/runloop中可以嵌套多个thread/runloop

iOS中非常著名的网络库AFNetworking(老版本,大家都喜欢拿这段代码进行解读)有这样一段代码:

+ (void)networkRequestThreadEntryPoint:(id)__unused object {
  @autoreleasepool {
    [[NSThread currentThread] setName:@"AFNetworking"];     NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
    [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
    [runLoop run];
  }
} + (NSThread *)networkRequestThread {   static NSThread *_networkRequestThread = nil;static dispatch_once_t oncePredicate;   dispatch_once(&oncePredicate, ^{
    _networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];
    [_networkRequestThread start];
  });   return _networkRequestThread;
}

这段代码创建了一个守护线程,为了让这个线程能够一直在后台处理网络,在线程入口networkRequestThreadEntryPoint: 中通过 [NSRunLoop currentRunLoop]创建了一个runloop;为了runloop能够一直跑圈,向runloop中加入了一个Mach port源,只要这个port源不移除,runloop就会一直存在。

这个时候的runloop其实是一直处于休眠状态,随后开始网络请求时向其中加入了connection源:

- (void)operationDidStart {
  self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO];
  NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
  for (NSString *runLoopMode in self.runLoopModes) {
    [self.connection scheduleInRunLoop:runLoop forMode:runLoopMode];
  }
  [self.connection start];
}
}

connection收到网络响应后,runloop被激活,开始跑圈,然后检测到有网络响应数据,runloop和connection协调数据输出,以回调的形式将底层接收的数据抛出,

当所有数据传输结束后,connection被AFNetworking置为ni,从runloop中移除,这时runloop进入休眠,这样就完成了一次在后台发请求、接收数据的过程。

等重新开始一次网络请求时,这个过程将会被再次执行一遍。

参考资料

Threading Programming Guide: Run Loops

Threading Programming Guide: Thread Management

runloop之于thread的更多相关文章

  1. 深入理解RunLoop

    网上看的一篇文章,写的真好,我得多看几次好好理解理解 膜拜大神,转载至此便于学习查看. 此处标明原文链接:http://blog.ibireme.com/2015/05/18/runloop/    ...

  2. 【转】Thread.isBackground

    C#中,Thread类有一个IsBackground 的属性.MSDN上对它的解释是:获取或设置一个值,该值指示某个线程是否为后台线程.个人感觉这样的解释等于没有解释. .Net中的线程,可以分为后台 ...

  3. 浅谈Runloop

    RunLoop 是 iOS 和 OS X 开发中非常基础的一个概念,这篇文章将从 CFRunLoop 的源码入手,介绍 RunLoop 的概念以及底层实现原理.之后会介绍一下在 iOS 中,苹果是如何 ...

  4. Runloop 深入理解(转)

    RunLoop 是 iOS 和 OSX 开发中非常基础的一个概念,这篇文章将从 CFRunLoop 的源码入手,介绍 RunLoop 的概念以及底层实现原理.之后会介绍一下在 iOS 中,苹果是如何利 ...

  5. RunLoop的深入了解

    RunLoop 是 iOS 和 OS X 开发中非常基础的一个概念,这篇文章将从 CFRunLoop 的源码入手,介绍 RunLoop 的概念以及底层实现原理.之后会介绍一下在 iOS 中,苹果是如何 ...

  6. 关于Thread.IsBackground属性的理解(转载)

    C#中,Thread类有一个IsBackground 的属性.MSDN上对它的解释是:获取或设置一个值,该值指示某个线程是否为后台线程.个人感觉这样的解释等于没有解释. .Net中的线程,可以分为后台 ...

  7. 史上最全的RunLoop介绍

    之前有人在后台给小编留言,说:小编啥时候给我们分享RunLoop的一些文章,工作以后特别需要这样的技术.这不,小编从网上找了一个介绍非常详细,清晰的文章,仅供参考. RunLoop 是 iOS 和 O ...

  8. Thread IsBcakgroud

    C#中,Thread类有一个IsBackground 的属性.MSDN上对它的解释是:获取或设置一个值,该值指示某个线程是否为后台线程.个人感觉这样的解释等于没有解释. .Net中的线程,可以分为后台 ...

  9. iOS 深入理解RunLoop

    RunLoop 是 iOS 和 OSX 开发中非常基础的一个概念,这篇文章将从 CFRunLoop 的源码入手,介绍 RunLoop 的概念以及底层实现原理.之后会介绍一下在 iOS 中,苹果是如何利 ...

随机推荐

  1. O(1)时间删除链表的已知结点

    这题并不需要从头结点遍历到已知结点,只需要知道已知结点,将改结点下一个结点赋值给它,再删除这个下一个结点就行,其中还需要考虑各种情况. 1)链表为空或者已知结点为空 2)链表只有一个结点,这个结点就是 ...

  2. centos7 rabbitmq系统部署

    CentOS7 1.安装系统 基础设施服务器:Java平台.Linux远程管理.开发工具 2.打开网络连接: (1)cd  /etc/sysconfig/network-scripts/  #进入网络 ...

  3. mysql的group_concat的用法

    1.语法:group_concat([DISTINCT] 要连接的字段 [Order BY ASC/DESC 排序字段] [Separator '分隔符']) eg. SELECT ID, GROUP ...

  4. Bnuoj-29359 Deal with numbers 线段树

    题目链接:http://www.bnuoj.com/bnuoj/problem_show.php?pid=29359 题意:一个数列,有三种操作: 1.区间[a,b]之间大于零的数整出c. 2.区间[ ...

  5. 基于samba实现win7与linux之间共享文件_阳仔_新浪博客

    基于samba实现win7与linux之间共享文件_阳仔_新浪博客 然后启动samba执行如下指令: /dev/init.d/smb start 至此完成全部配置.

  6. hdoj 2122 Ice_cream’s world III

    并查集+最小生成树 Ice_cream’s world III Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 ...

  7. [置顶] 解成电OJ1003真实的谎言的记录

    原题目 Description   N个人做一个游戏,游戏中每个人说了一句话(可能是真的也可能是假的) 第i个人说:“N个人中有至少有ai个,至多有bi个人说的是真话!”(i = 1, 2, 3….. ...

  8. android http 通信(httpclient 实现)

    1.httpclient get 方式 HttpGet httpGet = new HttpGet(url); HttpClient client = new DefaultHttpClient(); ...

  9. other

    1.http://handlebarsjs.com/ 2.grunt

  10. 【不积跬步,无以致千里】mysql 多行合并函数

    国内私募机构九鼎控股打造APP,来就送 20元现金领取地址:http://jdb.jiudingcapital.com/phone.html内部邀请码:C8E245J (不写邀请码,没有现金送)国内私 ...