iOS多线程-RunLoop简介

什么是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结束.

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

(#)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
复制代码

转载于:https://juejin.im/post/5b9635395188255c5121a4f4

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值