• 什么是RunLoop?

    • 从字面上来看是运行循环的意思.

    • 内部就是一个do{}while循环,在这个循环里内部不断的处理各种任务(比如:source/timer/Observer)

    • RunLoop的存在其实就是为线程而存在的.线程的作用就是执行一个特定的任务,但是默认情况下线程执行完任务后就不能再次执行任务,这是因为默认情况下线程是没有开启RunLoop的.如果开启RunLoop之后,线程执行完任务之后,会一直等待,直到再次接受到任务,接续执行任务.线程销毁前,会先释放这个线程所对应的RunLoop.

  • RunLoop基本作用

    • 保持程序的持续运行,保持线程的持续运行.

    • 处理App中的各种事件(比如触摸事件,定时器事件,Selector事件)

    • 节省CPU资源,提高程序性能:该做事时做事,该休息时休息

  • RunLoop对象

    • ios中有2套API来访问和使用RunLoop

    • 一套是Fundation(纯OC的)框架中的

      • NSRunLoop
        // 获得当前线程的RunLoop对象
        [NSRunLoop currentRunLoop];
        // 获得主线程的RunLoop对象
        [NSRunLoop mainRunLoop];
    • 一套是Core Fundation(纯C语言的)框架中的

      • CFRunLoopRef
        // 获得当前线程的RunLoop对象
        CFRunLoopGetCurrent();
        // 获得主线程的RunLoop对象
        CFRunLoopGetMain();
    • NSRunLoo和CFRunLoopRef都代表着RunLoop对象.NSRunLoop是基于CFRunLoopRef的一层OC包装

  • RunLoop与线程

    • 每条线程都有唯一的一个与之对应的RunLoop对象

    • 主线程的Runloop系统已经自动创建好了,子线程的RunLoop需要手动创建

    • RunLoop在第一次获取时由系统自动创建,在线程结束时销毁

    • 如果想给子线程创建RunLoop,不能直接alloc&init,只要调用获取当前线程RunLoop方法即可,系统会自动放回当前线程的RunLoop,如果当前线程没有RunLoop,系统会自动创建.

  • RunLoop相关类

  • Core Fundation中关于RunLoop的5个类

    • CFRunLoopRef: RunLoop对象
    • CFRunLoopModeRef: RunLoop运行模式.
    • CFRunLoopSoruceRef: 事件源(输入源)
    • CFRunLoopTimerRef:基于时间的触发器.
    • CFRunLoopObserverRef: 观察者,能够监听RunLoop的状态改变
  • CFRunLoopModeRef

  • CFRunLoopModeRef代表RunLoop运行模式

  • 一个RunLoop对象包含若干个Mode(模式),每个Mode又包含若干个 source/Timer/Observer

  • RunLoop运行时,只能指定一个Mode, 这个Mode又称之为CurrentMode,然后RunLoo就执行CurrentMode中的source/Timer/Observer

  • 如果需要切换Mode,只能退出RunLoop,再重新指定一个Mode进入,这样做是为了分隔开不同组的Source/Timer/Observer,让其不受影响

  • 系统默认注册了5个Mode:

    • NSDefaultRunLoopMode: App的默认Mode,通常主线程实在这个模式下运行
    • UITrackingRunLoopMode:界面跟踪Mode,用于界面控件(ScrollView,tableView等等)追踪触摸滑动,保证界面滑动时不受其他Mode影响
    • UIInitializationRunLoopMode:在刚启动App是进入的第一个Mode,启动完成后就不再使用
    • GSEventReceiveRunLoopMode:接收系统事件的内部Mode,通常用不到
    • NSRunLoopCommonMode:这是一个占位的Mode,不是一种真正的Mode,(可以看成模式组,默认情况下包括了NSDefaultRunLoopMode,UITrackingRunLoopMode)两种模式.
  • CFRunLoopTimerRef

    • CFRunLoopTimerRef是基于时间的触发器

    • CFRunLoopTimerRef基本上说的就是NSTimer,它受RunLoop的Mode影响

      -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
      
      //创建一个NSTimer定时器,默认情况下NSTimer是不会执行的,只有把NSTimer添加到RunLoop中,由RunLoop管理执行
      NSTimer * timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(show) userInfo:nil repeats:YES]; // 在当前线程中RunLoop添加一个timer, 并告诉runLoop, 这个timer只能在NSDefaultRunLoopMode模式下才能触发
      // runLoop会找到NSDefaultRunLoopMode,然后把timer添加NSDefaultRunLoopMode中的Timer数组中
      [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; //在当前线程中RunLoop添加一个timer, 并告诉runLoop, 这个timer只能在NSRunLoopCommonModes模式下才能触发
      //runLoop会找到NSDefaultRunLoopMode和UITrackingRunLoopMode
      //然后把timer添加NSDefaultRunLoopMode和UITrackingRunLoopMode中的Timer数组中
      [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; //利用此方法创建的NSTimer, 系统会自动放入当前线程中的currentRunLoop中,并且只能在NSDefaultRunLoop模式下才能触发
      NSTimer * timer1 = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(show) userInfo:nil repeats:YES];
      //虽然通过类方法scheduledTimerWithTimeInterval创建NSTimer,会自动添加到NSDefaultRunLoopMode模式中
      //但我们还是可以修改它的模式
      [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
      }
  • CFRunLoopSoruceRef

    • 按照官方文档,source的分类:

      • Port-Based Sources:基于端口的事件源:监听程序响应的端口,基于端口事件是由系统内核自动发送的.
      • Custom Input Sources: 自定义输入源:监听自定义事件源,而自定义的输入源是需要人工从其他线程发送
      • Cocoa Perfrom Selector Source: selector事件源
    • 按照源码函数调用栈,source的分类:

      • Source0:非基于Prot(端口)的,是用户主动触发的事件
      • Source1:基于Prot(端口)的,通过内核和其他线程相互发送消息
  • CFRunLoopObserverRef

    • CFRunLoopObserverRef:观察者对象,可以监听RunLoop的状态

    • RunLoop状态:

      • kCFRunLoopEntry 即将进入runLoop
      • kCFRunLoopBeforeTimers 即将处理Timer
      • kCFRunLoopBeforeSources 即将处理source(事件源)
      • kCFRunLoopBeforeWaiting 即将进入休眠
      • kCFRunLoopAfterWaiting 即将从休眠中醒来
      • kCFRunLoopExit 即将退出runLoop
      -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
      //创建一个CFRunLoopObserverRef
      /*
      第一个参数: CFRunLoopObserverRef(观察者)分配内存空间方式
      第二个参数: 监听那些状态 kCFRunLoopAllActivities(监听所有状态)
      第三个参数: 是否每次都要监听
      第四个参数: 优先级
      第五个参数: 回调函数
      */
      CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES,0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
      // observer监听对象
      //activity Runloop当前状态
      });
      /*
      第一个参数: 为那个线程下的RunLoop添加CFRunLoopObserverRef(观察者)
      第二个参数: 需要添加的CFRunLoopObserverRef(观察者)
      第三个参数: 把监听添加到RunLoop那个模式中
      */
      CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode); //记得内存管理,因为Core Foundation不在ARC管理范围内
      //带有Create、Copy、Retain等字眼的函数,创建出来的对象,都需要在最后做一次release
      //销毁对象函数:CFRelease对象
      CFRelease(observer);
      }
  • RunLoop处理逻辑

  • 如果RunLoop中没有Timer或source,RunLoop就会立刻退出

  • 每次运行RunLoop,RunLoop会自动处理之前未处理的消息,并通知相关观察者.具体顺序如下:

    • 1.通知观察者RunLoop已经启动

    • 2.通知观察者即将开始启动定时器

    • 3.通知观察者即将启动非基于端口的事件源

    • 4.启动任何准备好的非基于端口的事件源

    • 5.如果基于端口的事件源准备好并处于等待得状态,立即启动.并进入步骤9

    • 6.通知观察者线程进入休眠

    • 7.将线程置于休眠直到任意下面的事件发生:

      • 某一事件到达基于端口的源
      • 定时器启动
      • RunLoop设置的时间已经超时.(系统底层会给RunLoop设置一个超时时间,源码中设置的是:9999999999.0)
      • RunLoop被手动唤醒
    • 8.通知观察者线程将被唤醒.

    • 9.处理未处理的事件

      • 如果用户定义的定时器启动,处理定时器事件并重启RunLoop.进入步骤2
      • 如果事件源启动,传递相应的消息
      • 如果RunLooop被显示唤醒而且时间还没超时,重启RunLoop.进入步骤2
    • 10.通知观察者RunLoop结束.

  • 如何让子线程成为常驻线程(让一个子线程不进入消亡状态,等待其他线程发来消息,处理其他事件)

//
// ViewControllerRunLoop.m
//
// Created by yuxuan on 16/2/17.
// Copyright © 2016年 apple. All rights reserved.
// #import "ViewControllerRunLoop.h" @interface ViewControllerRunLoop () @property(nonatomic,strong)NSThread * thread; @end
@implementation ViewControllerRunLoop -(void)viewDidLoad{ //创建子线程执行任务
self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil]; [self.thread start]; } -(void)run{
NSLog(@"跑起来");
//默认情况下,子线程是不会常驻的
//只有子线程中runloop启动,并且runloop中有source或timer,才会常驻
//只有常驻线程才能再次执行任务,因为线程中有runloop来处理事件了
//子线程的runloop是需要手动创建的, 并且需要手动启动
NSRunLoop * rl = [NSRunLoop currentRunLoop]; //如果子线程的runloop没有 source / timer 的话, 哪么子线程的runloop会立即关闭
//在runLoop中添加一个timer
[NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(timerRun) userInfo:nil repeats:YES]; //启动runloop
[rl run]; //如果线程成为了常驻线程,你会发现,不会执行到这行代码
//也就是说这个方法不会执行完,
NSLog(@"end");
} -(void)timerRun{
NSLog(@"%s",__func__);
} -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ // 让子线程再次执行任务
[self performSelector:@selector(againRun) onThread:self.thread withObject:nil waitUntilDone:NO];
} -(void)againRun{ NSLog(@"再次跑起来");
} @end
原文链接:http://www.jianshu.com/p/94d61de9e139

iOS多线程-RunLoop简介的更多相关文章

  1. iOS 多线程GCD简介

    一.简介 1.1 GCD (Grand Central Dispatch )是Apple开发的一个多核编程的解决方法. Grand 含义是“伟大的.宏大的”,Central含义“中央的”,Dispat ...

  2. iOS多线程GCD简介(一)

    之前讲过多线程之NSOperation,今天来讲讲代码更加简洁和高效的GCD.下面说的内容都是基于iOS6以后和ARC下. Grand Central Dispatch (GCD)简介 Grand C ...

  3. iOS多线程GCD简介(二)

    在上一篇中,我们主要讲了Dispatch Queue相关的内容.这篇主要讲一下一些和实际相关的使用实例,Dispatch Groups和Dispatch Semaphore. dispatch_aft ...

  4. iOS多线程学习及总结

    能有份网上的存储资料,备以后提升及参考 iOS 多线程编程 简介 一.      iOS有三种多线程编程的技术,分别是: 1.        NSThread 2.        Cocoa NSOp ...

  5. iOS 多线程:『RunLoop』详尽总结

    1. RunLoop 简介 1.1 什么是 RunLoop? 可以理解为字面意思:Run 表示运行,Loop 表示循环.结合在一起就是运行的循环的意思.哈哈,我更愿意翻译为『跑圈』.直观理解就像是不停 ...

  6. iOS多线程技术方案

    iOS多线程技术方案 目录 一.多线程简介 1.多线程的由来 2.耗时操作的模拟试验 3.进程和线程 4.多线程的概念及原理 5.多线程的优缺点和一个Tip 6.主线程 7.技术方案 二.Pthrea ...

  7. iOS 多线程GCD的基本使用

    <iOS多线程简介>中提到:GCD中有2个核心概念:1.任务(执行什么操作)2.队列(用来存放任务) 那么多线程GCD的基本使用有哪些呢? 可以分以下多种情况: 1.异步函数 + 并发队列 ...

  8. iOS多线程的详情使用示例--简进祥

    大家都知道,在开发过程中应该尽可能减少用户等待时间,让程序尽可能快的完成运算.可是无论是哪种语言开发的程序最终往往转换成汇编语言进而解释成机器码来执行.但是机器码是按顺序执行的,一个复杂的多步操作只能 ...

  9. iOS多线程杂论

    iOS多线程的分布 (1) NSThread (2) NSOperation (3) GCD 现在对下面三个进行一个个的分析,希望那里说得不对的地方希望简友们帮我指点一二. 1,NSThread 优点 ...

随机推荐

  1. web工程常用路径的获取方法

    此文章是基于 搭建SpringMVC+Spring+Hibernate平台 一. 利用 Spring 取得web工程根路径 1. web.xml 中添加如下: <context-param> ...

  2. 封装系统(以封装Windows 7为例)

    安装步骤: 1.安装系统 2.启用Administrator帐户 3.进行简单的系统设置 4.系统精简 5.安装Adobe Flash Player 6.设置IE主页 7.在系统盘(C盘)创建Sysp ...

  3. chpasswd命令

    chpasswd命令是批量更新用户口令的工具,是把一个文件内容重新定向添加到/etc/shadow中.   语法 chpasswd(选项) 选项 -e:输入的密码是加密后的密文:   -h:显示帮助信 ...

  4. 深入java集合学习1-集合框架浅析

    前言 集合是一种数据结构,在编程中是非常重要的.好的程序就是好的数据结构+好的算法.java中为我们实现了曾经在大学学过的数据结构与算法中提到的一些数据结构.如顺序表,链表,栈和堆等.Java 集合框 ...

  5. AC日记——二叉堆练习3 codevs 3110

    3110 二叉堆练习3  时间限制: 3 s  空间限制: 128000 KB  题目等级 : 黄金 Gold 题解       题目描述 Description 给定N(N≤500,000)和N个整 ...

  6. [LeetCode] Two Sum 两数之和

    Given an array of integers, return indices of the two numbers such that they add up to a specific ta ...

  7. Oracle 表和表数据恢复

    1. 表恢复 对误删的表,只要没有使用 purge 永久删除选项,那么基本上是能从 flashback table 区恢复回来的. 数据表和其中的数据都是可以恢复回来的,记得 flashback ta ...

  8. Activity的生命周期

    Activity的生命周期 以往我们实现页面间的跳转都是实例化Intent类的对象,但是页面在我们眼前的出现与消失没有我们所看到的那么简单,它有一个复杂的生命周期,一个页面的出现,被覆盖,再次出现,被 ...

  9. 51nod 最小周长

    1283 最小周长 题目来源: Codility 基准时间限制:1 秒 空间限制:131072 KB 分值: 5 难度:1级算法题  收藏  关注 一个矩形的面积为S,已知该矩形的边长都是整数,求所有 ...

  10. DSO之光度标定

    光度标定(Photometric Camera Calibration)是DSO(Direct Sparse Odometry)论文中比较特别的一部分.常规的vSLAM不太考虑光度标定的问题.比如基于 ...