首先了解一下Runloop
,Run
是运行,Loop
是循环。
- 默认情况下主线程的
RunLoop
原理:我们在启动一个iOS程序的时候,系统会调用创建项目时自动生成的
main.m
的文件。main.m
文件如下所示:
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
其中
UIApplicationMain
函数内部帮我们开启了主线程的RunLoop
,UIApplicationMain
内部拥有一个无线循环的代码。上边的代码中开启RunLoop
的过程可以简单的理解为如下代码(实际当然不是这样):
int main(int argc, char * argv[]) {
BOOL running = YES;
do {
// 执行各种任务,处理各种事件
} while (running);
return 0;
}
从上边可看出,程序一直在
do-while
循环中执行,所以UIApplicationMain
函数一直没有返回,我们在运行程序之后程序不会马上退出,会保持持续运行状态。
RunLoop
实际上是一个对象,这个对象在循环中用来处理程序运行过程中出现的各种事件(比如说触摸事件、UI
刷新事件、定时器事件、Selector
事件),从而保持程序的持续运行;而且在没有事件处理的时候,会进入睡眠模式,从而节省CPU
资源,提高程序性能。从上图中可以看出,
RunLoop
就是线程中的一个循环,RunLoop
在循环中会不断检测,通过Input sources
(输入源)和Timer sources
(定时源)两种来源等待接受事件;然后对接受到的事件通知线程进行处理,并在没有事件的时候进行休息。
Runloop
的特点:
RunLoop
和线程是绑定在一起的,每条线程都有唯一一个与之对应的RunLoop
。- 不能创建
RunLoop
,但是可以获取系统提供的RunLoop
。- 主线程的
RunLoop
是由系统自动创建好的,在应用程序启动的时候会自动完成启动,而子线程中的RunLoop
需要手动获取并启动。
所以要注意主线程默认开启Runloop
,子线程默认不开启,如下面例子:
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"1");
[[NSRunLoop currentRunLoop] run];//子线程runloop默认不开启
[self performSelector:@selector(test) withObject:nil afterDelay:0];//内部是创建timer执行
NSLog(@"3");//不开启子线程的runloop就只打印1,3
});
-
Runloop的结构
平常使用的NSRunloop
内部其实就是CFRunloop
:
struct __CFRunLoop {
CFRuntimeBase _base;
pthread_mutex_t _lock;//获取mode列表的锁 /* locked for accessing mode list */
//mach_port
__CFPort _wakeUpPort;//唤醒端口 // used for CFRunLoopWakeUp
Boolean _unused;
volatile _per_run_data *_perRunData;//重置RunLoop数据 // reset for runs of the run loop
pthread_t _pthread;//RunLoop所对应的线程
uint32_t _winthread;
CFMutableSetRef _commonModes;//标记为common的mode的集合
CFMutableSetRef _commonModeItems;//commonMode的item集合
CFRunLoopModeRef _currentMode;//当前的mode
CFMutableSetRef _modes;//存储的是CFRunLoopModeRef
struct _block_item *_blocks_head;// _block_item链表表头指针
struct _block_item *_blocks_tail;// _block_item链表表尾指针
CFAbsoluteTime _runTime;//运行时间点
CFAbsoluteTime _sleepTime;//睡眠时间点
CFTypeRef _counterpart;
};
struct __CFRunLoopMode {
CFRuntimeBase _base;
pthread_mutex_t _lock;//锁, 必须runloop加锁后才能加锁 /* must have the run loop locked before locking this */
CFStringRef _name;//mode的名称
Boolean _stopped;//mode是否停止
char _padding[3];
CFMutableSetRef _sources0;//sources0事件
CFMutableSetRef _sources1;//sources1事件
CFMutableArrayRef _observers;//observers事件
CFMutableArrayRef _timers;//timers事件
CFMutableDictionaryRef _portToV1SourceMap;//字典 key是mach_port_t,value是CFRunLoopSourceRef
__CFPortSet _portSet;//保存所有需要监听的port,比如_wakeUpPort,_timerPort都保存在这个数组中
CFIndex _observerMask;
#if USE_DISPATCH_SOURCE_FOR_TIMERS
dispatch_source_t _timerSource;//GCD定时器
dispatch_queue_t _queue;//GCD队列
Boolean _timerFired;// 当定时器触发时设置为true // set to true by the source when a timer has fired
Boolean _dispatchTimerArmed;
#endif
#if USE_MK_TIMER_TOO
mach_port_t _timerPort;
Boolean _mkTimerArmed;
#endif
#if DEPLOYMENT_TARGET_WINDOWS
DWORD _msgQMask;
void (*_msgPump)(void);
#endif
uint64_t _timerSoftDeadline; /* TSR */
uint64_t _timerHardDeadline; /* TSR */
};
-
应用
最常见的例子应该是定时器了:
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
NSLog(@"1");
}];
[[NSRunLoop currentRunLoop] addTimer:timer forMode: NSRunLoopCommonModes];
下一篇:Runloop获取。
网友评论