概念
定义
事件处理循环,在此期间接收事件并将其发送给适当的处理者。
特点
没有事件处理时休眠,有事件处理时唤醒.
一个RunLoop对应多个RunLoopMode.
线程和RunLoo一一对应.
源码_CFRunLoopGet0中:
-
默认创建主线程的RunLoop,并开启.
-
全局变量保存个个线程和RunLoop对象的关系.
-
子线程RunLoop在获取的时候才创建,默认不开启.
-
RunLoop的生命周期与线程的一致.
RunLoop分析
定义
-
提供了两个这样的对象:NSRunLoop 和 CFRunLoopRef。
-
NSRunLoop 是基于 CFRunLoopRef 的封装,提供了面向对象的 API,但是这些 API 不是线程安全的。
-
CFRunLoopRef 是在 CoreFoundation 框架内的,它提供了纯 C 函数的 API,所有这些 API 都是线程安全的。
组件主要构成
struct __CFRunLoop
struct __CFRunLoop {
CFMutableSetRef _commonModes;
CFMutableSetRef _commonModeItems;
CFRunLoopModeRef _currentMode;
CFMutableSetRef _modes;
};
__CFRunLoopMode
struct __CFRunLoopMode {
CFStringRef _name;
CFMutableSetRef _sources0;
CFMutableSetRef _sources1;
CFMutableArrayRef _observers;
CFMutableArrayRef _timers;
};
RunLoop对象运行
CFRunLoopRun
RunLoop 其实内部就是do-while循环,在这个循环内部不断地处理各种任务(比如Source、Timer、Observer),通过判断result的值实现的
//Run
void CFRunLoopRun(void) { /* DOES CALLOUT */
int32_t result;
do {
result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
CHECK_FOR_FORK();
} while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}
源码得知:
- kCFRunLoopDefaultMode,默认情况下,runLoop是在这个mode下运行的,
- runLoop的运行主体是一个do..while循环,除非停止或者结束,否则runLoop会一直运行下去
CFRunLoopRunInMode
SInt32 CFRunLoopRunInMode(CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { /* DOES CALLOUT */
CHECK_FOR_FORK();
return CFRunLoopRunSpecific(CFRunLoopGetCurrent(), modeName, seconds, returnAfterSourceHandled);
}
源码得知:
该方法,可以设置runLoop运行在哪个mode下modeName,超时时间seconds,以及是否处理完事件就返回returnAfterSourceHandled。
这两个方法实际调用的是同一个方法CFRunLoopRunSpecific,其返回是一个SInt32类型的值,根据返回值,来决定runLoop的运行状况。
测试
1.子线程RunLoop默认不开启
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"1");
// 手动开启运行,要不然测试方法不响应.
[[NSRunLoop currentRunLoop] run];
[self performSelector:@selector(testRunLoop) withObject:nil afterDelay:0];
});
//测试方法
- (void)testRunLoop{
NSLog(@"testRunLoop")
}
官方文档performSelector:
This method sets up a timer to perform the aSelector message on the current thread’s run loop. The timer is configured to run in the default mode (NSDefaultRunLoopMode). When the timer fires, the thread attempts to dequeue the message from the run loop and perform the selector. It succeeds if the run loop is running and in the default mode; otherwise, the timer waits until the run loop is in the default mode.
由此可知此方法在当前线程的run loop中注册了timer事件,我们的代码即证明了子线程RunLoop默认不开启.
2.runloop的运行
1.我们经常会在应用中看到tableView 的header 上是一个横向ScrollView,一般我们使用NSTimer,每隔几秒切换一张图片。可是当我们滑动tableView的时候,顶部的scollView并不会切换图片,怎么办?
方法
- 将timer加入运行模式为common的runloop下.
- 在一个单独的子线程中,在当前的runloop中添加timer,runloop为默认模式即可.