这篇文章是在看了sunnyxx大神的线下分享后整理的学习笔记(文中部分图片出自视频,所以不太清晰,文末会放上视频链接)
概念
Runloop就像它的名字一样,就是跑环.我的理解就是一个死循环.是一个可以随时睡眠,随时唤醒的死循环
大家可以想一下,手机app为什么会一直运行?而且在接收到用户点击等等操作时就会有所反映.这个离不开runloop.
iOS app启动时就会启动一个runloop,而且这种模式应该Android也有,所以才会有了app能一直运行
每个线程都有一个runloop,但是只有主线程的runloop是默认开启的,其他子线程需要调用NSRunLoop *runloop = [NSRunLoop currentRunLoop];
获取runloop的同时就会创建runloop
一个线程可以创建多个runloop,但是只能是嵌套模式.也就是一个线程只有一个根runloop
runloop是由事件驱动的
这里区分下命令式驱动跟事件驱动
int main(int argc, char * argv[]) {
NSLog(@"hello world");
return 0;
}
int main(int argc, char * argv[]) {
while (AppIsRunning) {
id whoWakesMe = SleepForWakingUp;
id event = GetEvent(whoWakesMe);
HandleEvent(event);
}
return 0;
}
while (活着){
有事干了 = 我睡觉了没事别叫我();
if(该吃饭){
吃饭();
}else if(该上厕所){
上厕所();
}
}
与CFRunloop相关的有GCD,mach kernel是苹果内核的东西,还有block,pthread等
与咱们平时敲代码比较近一层有以下这些
static void __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__();
static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__();
static void __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__();
static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__();
static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__();
static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__();
几乎所有的函数都是从以上6中函数中调起.比如上图中就是调用的static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__();static void __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__()
kCFRunLoopEntry = (1UL << 0),// 即将进入Loop
kCFRunLoopBeforeTimers = (1UL << 1),// 即将处理 Timer
kCFRunLoopBeforeSources = (1UL << 2),// 即将处理 Source
kCFRunLoopBeforeWaiting = (1UL << 5),// 即将进入休眠
kCFRunLoopAfterWaiting = (1UL << 6),// 刚从休眠中唤醒
kCFRunLoopExit = (1UL << 7),// 即将退出Loop
kCFRunLoopAllActivities = 0x0FFFFFFFU//所有状态
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerTick:) userInfo:nil repeats:YES];
若不希望Timer被滑动影响,需添加到
NSRunLoopCommonMode
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerTick:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop]addTimer:timer forMode:NSRunLoopCommonModes];
//设定过期时间
SetupThisRunLoopRunTimeOutTimer(); //by GCD timer
do{
//通知Observer要跑timer跟source
__CFRunLoopDoObservers(kCFRunLoopBeforeTimers);
__CFRunLoopDoObservers(kCFRunLoopBeforeSources);
__CFRunLoopDoBlocks();
//运行到此刻,去检测当前加到消息队列source0的消息,此方法遍历source0去执行
__CFRunLoopDoSource0();
//询问GCD有没有分到主线程的东西需要调用
CheckIfExistMessageInMainDispatchQueue(); //GCD
//通知Observer要进入睡眠
__CFRunLoopDoObservers(kCFRunLoopBeforeWaiting);
//此刻获取到是哪个端口把我叫醒
var wakeUpPort = SleepAndWaitForWakingUpPorts();
// mach_msg_trap
// Zzz...
// Received mach_msg, wake up!
//通知Observer我要醒了~
__CFRunLoopDoObservers(kCFRunLoopAfterWaiting);
//Handler msgs
if(wakeUpPort == timerPort){
//如果是timer唤醒就去执行timer
__CFRunLoopDoTimer();
}else if(wakeUpPort == mainDispatchQueuePort){
//GCD需要我,就去调GCD的事件
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE();
}else{
//比如说网络来数据了就会用这个端口唤醒,然后做数据处理
__CFRunloopDoSource1();
}
__CFRunLoopDoBlocks();
}while (!stop && !timeOut);//如果没被外部干掉或者时间没到,继续循环
UIImage *downLoadImage = ...;
[self.avatarImageView performSelector:@selector(setImage:)
withObject:downloadImage
afterDelay:0
inModes:@[NSDefaultRunLoopMode]];
//创建RunLoop
CFRunLoopRef runloop = CFRunLoopGetCurrent();
//获取所有Mode,因为可能有很多Mode,每个Mode都需要跑,此处可以选择提交下崩溃信息之类的
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"程序崩溃了" message:@"崩溃信息" delegate:nil cancelButtonTitle:@"取消" otherButtonTitles:nil];
[alertView show];
NSArray *allModes = CFBridgingRelease(CFRunLoopCopyAllModes(runloop));
while (1) {
//快速切换Mode
for (NSString *mode in allModes) {
CFRunLoopRunInMode((CFStringRef)mode, 0.001, false);
}
}