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. Git如何把本地代码推送到远程仓库

    Git如何把本地代码推送到远程仓库 1. 初始化版本库 $ git init 2. 添加文件到版本库(只是添加到缓存区),.代表添加文件夹下所有文件 $ git add . 3. 把添加的文件提交到版 ...

  2. mysql 查询指定数据库中的表明和字段名

    SELECT TABLE_NAME,COLUMN_NAME,COLUMN_COMMENT FROM INFORMATION_SCHEMA.COLUMNS WHERE COLUMN_COMMENT LI ...

  3. 解决element-ui的表格设置固定栏后,边框线消失的bug

    如上图所示,边框线消失了,解决方法如下 添加css代码,如果是修改全局,则到全局样式文件添加 .el-table__row{ td:not(.is-hidden):last-child{ right: ...

  4. Html table 内容超出显示省略号

    内容超出显示省略号: <html> <style> table { table-layout: fixed; width: 100%; } table, th, td { bo ...

  5. Pyhton 连接数据库

    Python连接MySql 步骤 开始 创建connection 获取cursor 操作过程 SQL语句 执行查询 执行命令 获取数据 处理数据 关闭游标:cursor.close() 关闭连接:co ...

  6. Python3+Requests+Excel完整接口自动化框架

    框架整体使用Python3+Requests+Excel:包含对实时token的获取 框架结构图 1.------base -------runmethond.py runmethond:对不同的请求 ...

  7. JUC-4-CopyOnWriteArrayList

    什么是CopyOnWrite容器 CopyOnWrite容器即写时复制的容器.通俗的理解是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新 ...

  8. 0day2安全——笔记4(修改临界变量)

    第二章 修改临界变量 #include <stdio.h> #include <string.h> #define PASSWORD "1234567" i ...

  9. python中可变与不可变类型的全局变量

    python中的不可变类型的全局变量如int  a=1,str  b='hello', 若需要修改必须加global申明, 而全局变量是可变类型的,如list, dict ,则直接修改list.app ...

  10. HttpClient之用CloseableHttpClient发送post请求

    使用HttpClient发送请求的一般步骤(1) 创建HttpClient对象.(2)创建请求方法的实例,并指定请求URL.如果需要发送GET请求,创建HttpGet对象:如果需要发送POST请求,创 ...