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

最近重新梳理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. 2016CCPC 中南地区邀请赛 A 矩阵快速幂

    A A^n=A^(n%2016)%7; #include <iostream> #include <cstdio> #include <cstring> #incl ...

  2. javascript设计模式8

    桥接模式(将抽象与其实现隔离开来,以便二者独立变化) function sendInfo(element){ var id=element.id; ajax("GET"," ...

  3. Ubuntu 14.04.3 LTS 配置 DNS Server

    我们目的是用一台局域网机器完成 192.168.1.113 <-->cloudshield.com的解析,指定A记录和CNAME; 0.关于Ubuntu 14.04.2 LTS 下载.安装 ...

  4. C++学习笔记(八):函数重载、函数指针和函数对象

    函数重载 函数重载是指在同一作用域内,可以有一组具有相同函数名,不同参数列表的函数,这组函数被称为重载函数.重载函数通常用来命名一组功能相似的函数,这样做减少了函数名的数量,避免了名字空间的污染,对于 ...

  5. 如何在64位系统上安装SQL Server 2000

    如何在64位系统上安装SQL Server 2000? 现在用SQL Server 2000数据库的人少了吧?大都是SQL Server 2005/2008了.不过还是有需求的,今天一朋友就让我在他的 ...

  6. 剑指OFFER之第一个只出现一次的字符(九度OJ1283)

    题目描述: 在一个字符串(1<=字符串长度<=10000,全部由大写字母组成)中找到第一个只出现一次的字符. 输入: 输入有多组数据每一组输入一个字符串. 输出: 输出第一个只出现一次的字 ...

  7. Positioning(定位)

    Positioning(定位)    定位属性允许你为一个元素定位.它也可以将一个元素放在另一个元素后面,并指定一个元素的内容太大时,应该发生什么.    元素可以使用的顶部,底部,左侧和右侧属性定位 ...

  8. cocos2d-x 动画加速与减速

    转自:http://novacreo.com/%E7%A8%8B%E5%BA%8F%E7%BB%84/cocos2d-x%E5%8A%A8%E7%94%BB%E5%8A%A0%E9%80%9F%E4% ...

  9. c/c++中使用指针需要注意的问题

    一.使用指针的时候需要注意几点: 分配空间 初始化 释放 二.常见的错误有几种: 1)内存分配未成功,却使用了它      编程新手常犯这种错误,因为他们没有意识到内存分配会不成功.常用解决办法是,使 ...

  10. 【转】二叉树 VS hashtable

    hash_table和二叉搜索树都经常被用来构建符号表(或者字典)以及相关的结构,并且他们都表现出了很高的效率.最近也在不同的程序中使用了这两种数据结构,实现完毕后思考一下,对两者做了一个简单的比较: ...