一:RunLoop的简单介绍
#import "ViewController.h" @interface ViewController () @end @implementation ViewController /** * 1:Runloop和线程的关系:1:一一对应,主线程的runloop已经默认创建,但是子线程的需要手动创建:创建子线程的runloop: NSRunLoop *run = [NSRunLoop currentRunLoop];currentRunLoop懒加载的,在同一个子线程中创建多个runloop,则返回的都是同一个对象,因为其是懒加载模式的 2:在runloop中有多个运行模式,但是runloop只能选择一种模式运行,mode里面至少要有一个timer或者是source 2:1.获得主线程对应的runloop:NSRunLoop *mainRunLoop = [NSRunLoop mainRunLoop]; 2:获得当前线程对应的runLoop:NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop]; 3:CFRunLoop:1:获得主线程对应的runloop:CFRunLoopGetMain() 2:获得当前线程对应的runLoop:CFRunLoopGetCurrent() * */ -(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent *)event { //1.获得主线程对应的runloop NSRunLoop *mainRunLoop = [NSRunLoop mainRunLoop]; //2.获得当前线程对应的runLoop NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop]; NSLog(@"%p---%p",mainRunLoop,currentRunLoop); // NSLog(@"%@",mainRunLoop); //Core NSLog(@"%p",CFRunLoopGetMain()); NSLog(@"%p",CFRunLoopGetCurrent()); NSLog(@"%p",mainRunLoop.getCFRunLoop); //Runloop和线程的关系 //一一对应,主线程的runloop已经创建,但是子线程的需要手动创建 [[[NSThread alloc]initWithTarget:self selector:@selector(run) object:nil] start]; } //在runloop中有多个运行模式,但是runloop只能选择一种模式运行 //mode里面至少要有一个timer或者是source -(void)run { //如何创建子线程对应的runLoop,currentRunLoop懒加载的 NSLog(@"%@",[NSRunLoop currentRunLoop]); NSLog(@"%@",[NSRunLoop currentRunLoop]); NSLog(@"run---%@",[NSThread currentThread]); } @end
#import "ViewController.h" @interface ViewController () @end @implementation ViewController /** * 1:NSLog(@"%@",[NSRunLoop currentRunLoop]);打印当前线程的RunLoop,懒加载模式,一条线程对应一个RunLoop对象,有返回,没有创建,主线程的RunLoop默认创建,子线程的RunLoop需要手动创建,[NSRunLoop currentRunLoop],同一个线程中若是创建多个RunLoop,则返回的都是同一个RunLoop对象,一个RunLoop里会有多个mode运行模式(系统提供了5个),但运行时只能指定一个RunLoop,若是切换RunLoop,则需要退出当前的RunLoop 2:定时器NSTimer问题:1:若是创建定时器用timerWithTimeInterval,则需要手动将定时器添加到NSRunLoop中,指定的运行模式为default,但是如果有滚动事件的时候,定时器就会停止工作。解决办法:更改NSRunLoop的运行模式,UITrackingRunLoopMode界面追踪,此模式是当只有发生滚动事件的时候才会开启定时器。若是任何时候都会开启定时器: NSRunLoopCommonModes, NSRunLoopCommonModes = NSDefaultRunLoopMode + UITrackingRunLoopMode 占用,标签,凡是添加到NSRunLoopCommonModes中的事件爱你都会被同时添加到打上commmon标签的运行模式上 3:1:scheduledTimerWithTimeInterval此方法创建的定时器默认加到了NSRunLoop中,并且设置运行模式为默认。 2:若是想在子线程开启NSRunLoop:需要手动开启:NSRunLoop *currentRunloop = [NSRunLoop currentRunLoop];等到线程销毁的时候currentRunloop对象也随即销毁。2:在子线程的定时器,需要手动加入到runloop:不要忘记调用run方法 NSRunLoop *currentRunloop = [NSRunLoop currentRunLoop]; //该方法内部自动添加到runloop中,并且设置运行模式为默认 [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES]; //开启runloop [currentRunloop run]; /* * */ -(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent *)event { // NSLog(@"%@",[NSRunLoop currentRunLoop]); // [self timer2]; // [NSThread detachNewThreadSelector:@selector(timer2) toTarget:self withObject:nil]; [self timer1]; } -(void)timer1 { //1.创建定时器 NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES]; //2.添加定时器到runLoop中,指定runloop的运行模式为NSDefaultRunLoopMode /* 第一个参数:定时器 第二个参数:runloop的运行模式 */ // [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; //UITrackingRunLoopMode:界面追踪 [[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode]; // NSRunLoopCommonModes = NSDefaultRunLoopMode + UITrackingRunLoopMode //占用,标签,凡是添加到NSRunLoopCommonModes中的事件爱你都会被同时添加到打上commmon标签的运行模式上 /* 0 : {contents = "UITrackingRunLoopMode"} 2 : */ // [[NSRunLoop currentRunLoop]addTimer:timer forMode:NSRunLoopCommonModes]; } -(void)timer2 { NSRunLoop *currentRunloop = [NSRunLoop currentRunLoop]; //该方法内部自动添加到runloop中,并且设置运行模式为默认 [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES]; //开启runloop [currentRunloop run]; } -(void)run { NSLog(@"run-----%@---%@",[NSThread currentThread],[NSRunLoop currentRunLoop].currentMode); } @end{contents = "kCFRunLoopDefaultMode"
####1.Runloop基础知识
- 1.1 字面意思
a 运行循环
b 跑圈
- 1.2 基本作用(作用重大)
a 保持程序的持续运行(ios程序为什么能一直活着不会死)
b 处理app中的各种事件(比如触摸事件、定时器事件【NSTimer】、selector事件【选择器·performSelector···】)
c 节省CPU资源,提高程序性能,有事情就做事情,没事情就休息
- 1.3 重要说明
(1)如果没有Runloop,那么程序一启动就会退出,什么事情都做不了。
(2)如果有了Runloop,那么相当于在内部有一个死循环,能够保证程序的持续运行
(2)main函数中的Runloop
a 在UIApplication函数内部就启动了一个Runloop
该函数返回一个int类型的值
b 这个默认启动的Runloop是跟主线程相关联的
- 1.4 Runloop对象
(1)在iOS开发中有两套api来访问Runloop
a.foundation框架【NSRunloop】
b.core foundation框架【CFRunloopRef】
(2)NSRunLoop和CFRunLoopRef都代表着RunLoop对象,它们是等价的,可以互相转换
(3)NSRunLoop是基于CFRunLoopRef的一层OC包装,所以要了解RunLoop内部结构,需要多研究CFRunLoopRef层面的API(Core Foundation层面)
- 1.5 Runloop参考资料
```objc
(1)苹果官方文档
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html
(2)CFRunLoopRef开源代码下载地址:
http://opensource.apple.com/source/CF/CF-1151.16/
```
- 1.6 Runloop与线程
1.Runloop和线程的关系:一个Runloop对应着一条唯一的线程
问题:如何让子线程不死
回答:给这条子线程开启一个Runloop
2.Runloop的创建:主线程Runloop已经创建好了,子线程的runloop需要手动创建
3.Runloop的生命周期:在第一次获取时创建,在线程结束时销毁
- 1.7 获得Runloop对象
```objc
1.获得当前Runloop对象
//01 NSRunloop
NSRunLoop * runloop1 = [NSRunLoop currentRunLoop];
//02 CFRunLoopRef
CFRunLoopRef runloop2 = CFRunLoopGetCurrent();
2.拿到当前应用程序的主Runloop(主线程对应的Runloop)
//01 NSRunloop
NSRunLoop * runloop1 = [NSRunLoop mainRunLoop];
//02 CFRunLoopRef
CFRunLoopRef runloop2 = CFRunLoopGetMain();
3.注意点:开一个子线程创建runloop,不是通过alloc init方法创建,而是直接通过调用currentRunLoop方法来创建,它本身是一个懒加载的。
4.在子线程中,如果不主动获取Runloop的话,那么子线程内部是不会创建Runloop的。可以下载CFRunloopRef的源码,搜索_CFRunloopGet0,查看代码。
5.Runloop对象是利用字典来进行存储,而且key是对应的线程Value为该线程对应的Runloop。
```
- 1.8 Runloop相关类
(1)Runloop运行原理图
![PNG](2.png)
(2)五个相关的类
a.CFRunloopRef
b.CFRunloopModeRef【Runloop的运行模式】
c.CFRunloopSourceRef【Runloop要处理的事件源】
d.CFRunloopTimerRef【Timer事件】
e.CFRunloopObserverRef【Runloop的观察者(监听者)】
(3)Runloop和相关类之间的关系图
![PNG](1.png)
(4)Runloop要想跑起来,它的内部必须要有一个mode,这个mode里面必须有source\observer\timer,至少要有其中的一个。
- CFRunloopModeRef
1.CFRunloopModeRef代表着Runloop的运行模式
2.一个Runloop中可以有多个mode,一个mode里面又可以有多个source\observer\timer等等
3.每次runloop启动的时候,只能指定一个mode,这个mode被称为该Runloop的当前mode
4.如果需要切换mode,只能先退出当前Runloop,再重新指定一个mode进入
5.这样做主要是为了分割不同组的定时器等,让他们相互之间不受影响
6.系统默认注册了5个mode
a.kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行
b.UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
c.UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用
d.GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到
e.kCFRunLoopCommonModes: 这是一个占位用的Mode,不是一种真正的Mode