RunLoop字面上的意思是,运行循环;

其基本作用:保持程序的持续运行;

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

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

1.main函数中的RunLoop

int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
} 在这个main函数中,UIApplicationMain函数内部就启动了一个RunLoop,所以UIApplicationMain函数一直没有返回,保持了程序的持续运行。这个默认启动的RunLoop是跟主线程相关联的。

NSRunLoop 是基于CFRunLoopRef的一层OC包装,所以了解RunLoop内部结构,需要多研究CFRunLoopRef层面的API。

2.RunLoop与线程的关系

每条线程都有唯一的一个与之对应的RunLoop对象
主线程的RunLoop已经自动创建好了,子线程的RunLoop需要主动创建
RunLoop在第一次获取时创建,在线程结束时销毁
获取子线程对应的RunLoop(即,currentRunLoop)该方法本身是懒加载的,如果第一次调用就会创建当前线程对应的RunLoop并保存,以后调用则直接获取

3.RunLoop的获取

#pragma mark - RunLoop
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
//OC语言中的API
//01 获取主线程对应的runloop对象
NSRunLoop *mainRunloop = [NSRunLoop mainRunLoop];
//02 获取当前的runloop的对象
NSRunLoop *currentRunloop = [NSRunLoop currentRunLoop];
NSLog(@"%p---%p", mainRunloop, currentRunloop); //C语言的API
//01 主运行循环
CFRunLoopRef mainRunloopRef = CFRunLoopGetMain();
//02 当前的运行循环
CFRunLoopRef currentRunloopRef = CFRunLoopGetCurrent();
NSLog(@"%p---%p", mainRunloopRef, currentRunloopRef); //转化
NSLog(@"%p----%p", mainRunloop.getCFRunLoop, mainRunloopRef);
}

打印的结果:

-- ::31.176193+ NSCach[:] 0x60000184c060---0x60000184c060
-- ::31.176302+ NSCach[:] 0x600000050600---0x600000050600
-- ::31.176362+ NSCach[:] 0x600000050600----0x600000050600

可以看出,当前的主运行循环和当前运行循环是同一个runloop, 最后一行可以看出,OC获取的runloop与C的runloop可以相互转化。

4.RunLoop的模式与Timer的关系

//Runloop的运行模式和Timer
- (void)RunLoopModeAndTimer {
//创建定时器对象
NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
//添加到runloop中
//Mode:runloo的运行模式(默认|界面跟踪|占位)
//把定时器对象添加到runloop中,并制定运行模式为默认--当运行模式为NSDefaultRunLoopMode的时候,定时器才会工作
//[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; //当滚动textView的时候,主运行循环会切换运行模式:从默认切换成界面跟踪运行模式
//把定时器对象添加到runloop中,并制定运行模式为界面跟踪--当运行模式为UITrackingRunloopMode的时候(即拖拽界面时),定时器才会工作
//[[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode]; //当两种模式都可以运行时,均可用
//把定时器对象添加到runloop中,并制定运行模式为NSRunLoopCommonModes--当运行模式为NSDefaultRunLoopMode或者UITrackingRunloopMode的时候,定时器才会工作
//需要注意没有NSRunLoopCommonModes这种模式,NSRunLoopCommonModes是标记NSDefaultRunLoopMode或者UITrackingRunloopMode的集合
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
} - (void)run {
NSLog(@"run----%@", [NSRunLoop currentRunLoop].currentMode);
}

注意

//该方法内部会自动创建的定时器对象添加到当前的runloop,并且指定运行模式为默认
// [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES]; //如果想利用上面的那种方法,修改运行模式,可修改成如下
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];

注意:上面添加的定时器,如果是添加在主线程中的,所以,不需要开始runloop,定时器就可以工作;但是如果,直接添加到子线程中,例如下面的代码:

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {

[self performSelectorInBackground:@selector(RunLoopModeAndTimer) withObject:nil];

}

- (void)RunLoopModeAndTimer {

   //该方法内部会自动创建的定时器对象添加到当前的runloop,并且指定运行模式为默认
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES]; } - (void)run {
NSLog(@"run----%@", [NSRunLoop currentRunLoop].currentMode);
}

那么,点击界面的时候,定时器是不工作的;因为定时器对象添加到当前的runloop中,而当前的runloop在子线程中;子线程中的runloop需要手动创建,所以此时定时器不工作。

解决方式:开启runloop

- (void)RunLoopModeAndTimer {

   //该方法内部会自动创建的定时器对象添加到当前的runloop,并且指定运行模式为默认
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] run];
}

5.GCD的定时器

按照上面那段代码执行后,会发现,timer并没有执行。因为在程序运行到 } 后,代码就执行完了,就被释放了。所以需要添加一个强引用。

修改如下:

总结:

Runloop的相关类

Core Foundation中关于RunLoop的5个类:
CFRunLoopRef
CFRunLoopModeRef
CFRunLoopSourceRef
CFRunLoopTimerRef
CFRunLoopObserverRef

   当runloop选择一个运行模式后,需要判断运行模式是否为空,判断的依据是:Source和Timer是否有数据,如果没有数据,runloop立马退出。

选择了一个运行模式后

如果source或者timer有数据时,就开启runloop。

RunLoopObserver简单说明

  CFRunLoopObserverRef是观察者,能够监听RunLoop的状态改变。

  可以监听的时间点有以下几个:

RunLoop的知识小记的更多相关文章

  1. Runloop基础知识

    *:first-child { margin-top: 0 !important; } body > *:last-child { margin-bottom: 0 !important; } ...

  2. EF基础知识小记四(数据库=>模型设计器)

    EF基础知识小记三(设计器=>数据库)介绍了如何创建一个空设计器模型,并如何将模型同步到数据库的表中,本文则主要介绍如何将一个存在的数据库同步到模型设计器中.为了能快速的模拟这个过程,给出一下建 ...

  3. Linq基础知识小记四之操作EF

    1.EF简介 EF之于Linq,EF是一种包含Linq功能对象关系映射技术.EF对数据库架构和我们查询的类型进行更好的解耦,使用EF,我们查询的对象不再是C#类,而是更高层的抽象:Entity Dat ...

  4. RunLoop总结:RunLoop基础知识

    没有实际应用场景,很难理解一些抽象空洞的东西,所以前面几篇文章先介绍了RunLoop的几个使用场景. 另外AsyncDisplayKit中也有大量使用RunLoop的示例. 关于实际的使用RunLoo ...

  5. RunLoop相关知识的总结

    RunLoop 即运行循环,也叫事件循环,本质为一个死循环.iOS一个程序运行起来之后,默认会开启一个运行循环,有需要处理的操作时,比如用户的输入事件时,RunLoop会自己跑起来运行,没有需要处理的 ...

  6. IOS知识小记

    iOS开发 小知识点 http://www.cnblogs.com/tangbinblog/archive/2012/07/20/2601324.html Objective-C中的instancet ...

  7. RunLoop相关知识

    RunLoop,翻译过来是运行环路.我们在创建命令行项目和创建ios项目时,发现命令行项目当最后一行代码执行完后项目就自动退出了,而ios项目确可以一直运行,知道用户手动点击退出按钮.这就是因为ios ...

  8. RunLoop基础知识以及GCD

    - 1.1 字面意思   a 运行循环   b 跑圈   - 1.2 基本作用(作用重大)   a 保持程序的持续运行(ios程序因而能一直活着不会死)    b 处理app中的各种事件(比如触摸事件 ...

  9. java基本知识小记(1)

    1.Java中的值传递 值传递意味着对于传给方法的每个参数都会制作一份副本然后将副本而不是原始值传递给方法并且通过参数的名进行引用. 注意:虽然传值机制对于所有参数类型都适用,但是他对对象类型的作用与 ...

随机推荐

  1. C#面向对象--封装

    一.抽象和封装是面向对象编程的基础特性,抽象用来忽略细节,在不同的层次上处理细节,封装则实现了对细节的不同程度的访问权限:即抽象允许相关信息可视化,封装用来实现所需级别的抽象: 1.根据封装的原则,命 ...

  2. js基本操作

    js操作页面三步骤 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> < ...

  3. C#控制内插字符串的格式

    C#6.0推出了内插字符串 结果展示: 内插表达式字段宽度和对齐方式: 结果展示:(+/-代表右对齐.左对齐,数字表示显示宽度)

  4. 剑指offer笔记面试题11----旋转数组的最小数字

    题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转.输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素.例如,数组{3, 4, 5, 1, 2}为{1, 2, 3, 4, 5 ...

  5. LeetCode刷题191120

    博主渣渣一枚,刷刷leetcode给自己瞅瞅,大神们由更好方法还望不吝赐教.题目及解法来自于力扣(LeetCode),传送门. 算法: 给出两个 非空 的链表用来表示两个非负的整数.其中,它们各自的位 ...

  6. innodb存储引擎监控

    innodb存储引擎监控分为四种,表监控,表空间监控,锁监控,状态监控.可以在mysql客户端开启监控选项,然后信息将会输出在mysql的错误日志内.innodb_monitor和innodb_loc ...

  7. PHP对URL进行字符串编码

    urlencode($url1) urldecode($url) //对URL进行字符串编码和解码 $url1 = 'https://www.baidu.com/uploade/img/123.png ...

  8. MySQL中if的写法

    关键词:IF --if 判断,如果 device_num 的值为1,则赋值为100,反之为200 SELECT district, shop_name, IF ( device_num , , ) A ...

  9. start_udev 是不是会写磁盘头

    遇到一个案例,在这里记录一下 一套Oracle 11.2.0.4 RAC环境,操作系统是RHEL 6.5,共享磁盘是通过UDEV实现RAW绑定设备名,如下 [root@rac1 opt]# ll /d ...

  10. Java成员变量和局部变量区别

    成员变量和局部变量区别 变量根据定义位置的不同,我们给变量起了不同的名字.如下图所示: 区别 在类中的位置不同  (重点) 成员变量:类中,方法外 局部变量:方法中或者方法声明上(形式参数) 作用范围 ...