一、Event Loop & Cocoa RunLoop
宏观上:Event Loop
1.RunLoop是一个用于循环监听和处理事件或者消息的模型,接收请求,然后派发给相关的处理模块。
2.Cocoa RunLoop属于EventLoop模型在Mac平台的具体实现
微观上: Cocoa RunLoop
1.Cocoa RunLoop本质上就是一个对象,提供一个入口函数启动事件循环,在满足特点条件后才会退出。
2.Cocoa RunLoop与普通while/for循环不同的是它能监听处理事件和消息,能智能休眠和被唤醒,这些功能的实现依赖于Mac Port。
二、 Cocoa RunLoop的内部结构
RunLoop架构划分为四个部分:
1.事件源
2.运行模式
3.循环机制
4.执行反馈
1. 事件源
Cocoa RunLoop接受的事件源分为两种类型:Input Sources 和 Timer Sources
1.1. Input Sources
Input Sources通过异步派发的方式将事件转送到目标线程,事件类别分为两大块:
Port-Based Sources :
基于Mach端口的事件源,Cocoa和Core Foundation这两个框架已经提供了内部支持,只需要调用端口相关的对象或者函数就能提供端口进行通信。比如:将NSPort对象部署到RunLoop中,实现两个线程的循环通信。
Custom Input Sources:
用户自定义的输入源:使用Core Foundation框架中CFRunLoopSourceRef对象的相关函数实现。
Cocoa Perform Selector Sources:Cocoa框架内部实现的自定义输入源,可以跨线程调用,实现线程间通信,有点类似于Port-Based事件源,不同的是这种事件源只在RunLoop上部署一次,执行结束后便会自动移除。如果目标线程中没有启动RunLoop也就意味着无法部署着泪事件源,因此不会得到预期的结果。
使用Cocoa自定义事件源的函数接口,如下:
//部署在主线程
//参数列表:Selector:事件源处理函数,Selector参数,是否阻塞当前线程,指定RunLoop模式
performSelectorOnMainThread:withObject:waitUntilDone:
performSelectorOnMainThread:withObject:waitUntilDone:modes:
//部署在指定线程
//参数列表:Selector:事件源处理函数,指定线程,Selector参数,是否阻塞当前线程,指定RunLoop模式
permSelector:onThread:withObject:waitUntilDone:
performSelector:onThread:withObject:waitUntilDone:modes:
//部署在当前线程
//参数列表:Selector:事件源处理函数,Selector参数,延时执行时间,指定RunLoop模式
performSelector:withObject:afterDelay:
performSelector:withObject:afterDelay:inModes:
//撤销某个对象通过函数performSelector:withObject:afterDelay:部署在当前线程的全部或者指定事件源
cancelPreviousPerformRequestsWithTarget:
cancelPreviousPerformRequestsWithTarget:selector:object:
综上,Input Sources包括基于Mach端口的事件源和自定义的事件源,二者的唯一区别在于被触发的方式:前者是有内核自动触发,后者则需要在其他线程中手动触发。
1.2. Timer Sources
不同于Input Source的异步派发,Timer Source是通过派发的方式,在预设时间到达时将事件转送到目标线程。这种事件源可用于线程的自我提醒功能,实现周期性的任务。
如果RunLoop当前运行模式没有添加Time Source,则在Run Loop中部署的定时器不会被执行。
设定的间隔时间于真实的触发时间之间没有必然联系,定时器会根据设定的间隔时间周期性的派发消息到RunLoop,但是真实的触发时间由RunLoop决定,假设RunLoop当前正在处理奇异果长时间的任务,则触发时间会被延迟,如果在最终触发之前Timer已经派发了N个消息,RunLoop也只会当做一次派发对待,触发一次对应的处理函数。
2. 运行模式
运行模式类似于一个过滤器,用于屏蔽那些不关心的事件源,让RunLoop专注于监听和处理指定的事件源和RunLoop Observer。
CDRunLoopMode和CFRunLoop的数据结构大致如下:
struct __CFRunLoop {
CFMutableSetRef _commonModes; // Set
CFMutableSetRef _commonModeItems; // Set<Source/Observer/Timer>
CFRunLoopModeRef _currentMode; // Current Runloop Mode
CFMutableSetRef _modes; // Set
...
};
struct __CFRunLoopMode {
CFStringRef _name; // Mode Name, 例如 @"kCFRunLoopDefaultMode"
CFMutableSetRef _sources0; // Set
CFMutableSetRef _sources1; // Set
CFMutableArrayRef _observers; // Array
CFMutableArrayRef _timers; // Array
...
};
结合以上源吗:总结如下:
每种模式通过name属性作为标识。
一种运行模式(Run Loop Mode)就是一个集合,包含需要监听的事件源Input Sources和Timer Sources以及需要触发的RunLoop observers。
RunLoop简介:
运行循环,在程序运行过程中循环做一些事情(如接收消息、处理消息、休眠等待等);
RunLoop是通过内部维护的事件循环来对事件/消息进行管理的一个对象;
RunLoop不是一个简单的do…while循环,它涉及到用户态和内核之间的切换。
事件循环:
事件循环就是对事件/消息进行管理,事件循环可以达到:
没有消息需要处理时,休眠线程以避免资源占用。从用户态切换到内核态,等待消息;
有消息需要处理时,立刻唤醒线程,回到用户态处理消息;
通过调用mach_msg()函数类转移当前线程的控制权给内核态/用户态。
RunLoop的应用范畴
定时器(Timer)、Performselector
GCD:dispatch_async(dispatch_get_main_queue(),^{});
事件响应、手势识别、界面刷新
网络请求
AuterreleasePool
RunLoop 对象
ios中有2套API来访问和使用RunLoop:
1、Foundation:NSRunLoop(是CFRunLoopRef 的封装,提供了面向对象的API)
2.Core Foundation;CFRunLoopRef
NSRunLoop 和 CFRunLoopRef
NSRunLoop和CFRunLoopRef都代表着RunLoop对象
NSRunLoop不开源,而CFRunLoopRef是开源的:Core Foundation 源码
- 获取
RunLoop
对象的方式:
// Foundation
[NSRunLoop mainRunLoop]; // 获取主线程的 RunLoop 对象
[NSRunLoop currentRunLoop]; // 获取当前线程的 RunLoop 对象
// Core Foundation
CFRunLoopGetMain(); // 获取主线程的 RunLoop 对象
CFRunLoopGetCurrent(); // 获取当前线程的 RunLoop 对象
RunLoop在实际开发中的应用
1.使用端口或自定义输入源与其他线程进行通信
2.在子线程上使用定时器
3.解决NSTimer在滑动时停止工作的问题
4.控制线程的生命周期,实现一个常驻线程
5.在Cocoa应用程序中使用任何performSelector…方法
监控应用卡顿
性能优化
…