开发文章

iOS的Runloop深入了解

1 什么是Runloop

从字面意思看,Runloop的意思就是
运行循环,跑圈

Runloop基本作用:

  • 1.保持程序的持续运行
  • 2.处理App中的各种事件(比如触摸事件、定时器事件、Selector事件)
  • 3.节省CPU资源,提高程序性能:该做事时做事,该休息时休息

如果没有Runloop

复制内容到剪贴板
  1. int main(int argc, char * argv[]) {  
  2.     NSLog(@"execute main function");//程序开始  
  3.     return 0;//程序结束  
  4. }  

Runloop与线程

每条线程都有唯一的一个与之对应的RunLoop对象

主线程的RunLoop已经自动创建好了,子线程的RunLoop需要主动创建

RunLoop在第一次获取时创建,在线程结束时销毁

2 Runloop对象

iOS中有2套API来访问和使用RunLoop

  1. Foundation

    • NSRunLoop
  2. Core Foundation

    • CFRunLoopRef

NSRunLoop和CFRunLoopRef都代表着RunLoop对象

  • NSRunLoop是基于CFRunLoopRef的一层OC包装,所以要了解RunLoop内部结构,需要多研究CFRunLoopRef层面的API(Core Foundation层面)

  • Foundation

[NSRunLoop currentRunLoop]; // 获得当前线程的RunLoop对象 [NSRunLoop mainRunLoop]; // 获得主线程的RunLoop对象
  • Core Foundation
CFRunLoopGetCurrent(); // 获得当前线程的RunLoop对象 CFRunLoopGetMain(); // 获得主线程的RunLoop对象

3 Runloop相关的类

Core Foundation中关于RunLoop的5个类

  • CFRunLoopRef
  • CFRunLoopModeRef 【Runloop的运行模式】
  • CFRunLoopSourceRef 【Runloop要处理的事件源】
  • CFRunLoopTimerRef 【Timer事件】
  • CFRunLoopObserverRef 【Runloop的观察者(监听者)】

CFRunLoopRef

  • CFRunLoopModeRef代表RunLoop的运行模式
    一个 RunLoop 包含若干个 Mode,每个Mode又包含若干个Source/Timer/Observer

  • 每次RunLoop启动时,只能指定其中一个 Mode,这个Mode被称作 CurrentMode

  • 如果需要切换Mode,只能退出Loop,再重新指定一个Mode进入

  • 这样做主要是为了分隔开不同组的Source/Timer/Observer,让其互不影响

系统默认注册了5个Mode:

  • kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行

  • UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响

    • 典型事例就是拖动scrollview时,NSTimer不工作,其原因就是,拖动时runloop切换到界面追踪模式,此时其他模式的事件(例如定时器事件)就无法继续。
  • UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用

  • GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到

  • kCFRunLoopCommonModes: 这是一个占位用的Mode,不是一种真正的Mode

CFRunLoopSourceRef

CFRunLoopSourceRef是事件源(输入源)

  • 以前的分法

    • Port-Based Sources
    • Custom Input Sources
    • Cocoa Perform Selector Sources
  • 现在的分法

    • Source0:非基于Port的
    • Source1:基于Port的

CFRunLoopTimerRef

  • CFRunLoopTimerRef是基于时间的触发器

  • 基本上说的就是NSTimer

CFRunLoopObserverRef

  • CFRunLoopObserverRef是观察者,能够监听RunLoop的状态改变

  • 可以监听的时间点有以下几个

复制内容到剪贴板
  1. typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {  
  2.     kCFRunLoopEntry = (1UL << 0),   //即将进入Runloop  
  3.     kCFRunLoopBeforeTimers = (1UL << 1),    //即将处理NSTimer  
  4.     kCFRunLoopBeforeSources = (1UL << 2),   //即将处理Sources  
  5.     kCFRunLoopBeforeWaiting = (1UL << 5),   //即将进入休眠  
  6.     kCFRunLoopAfterWaiting = (1UL << 6),    //刚从休眠中唤醒  
  7.     kCFRunLoopExit = (1UL << 7),            //即将退出runloop  
  8.     kCFRunLoopAllActivities = 0x0FFFFFFFU   //所有状态改变  
  9. };  

4 重点-Runloop运行逻辑

Runloop运行逻辑.png

 

Runloop事件队列.png

5 重点-Runloop应用

1)NSTimer
2)ImageView显示:控制方法在特定的模式下可用
3)PerformSelector
4)常驻线程:在子线程中开启一个runloop
5)自动释放池的释放时机:
第一次创建:进入runloop的时候
最后一次释放:runloop退出的时候
其它创建和释放:当runloop即将休眠的时候会把之前的自动释放池释放,然后重新创建一个新的释放池

Runloop应用.png

5 重点-Runloop应用

  • 监听Runloop,可以在按钮响应方法之前做些事情
复制内容到剪贴板
  1. -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event  
  2. {  
  3.     [self observer];  
  4. }  
  5. -(void)observer  
  6. {  
  7.     //1.创建监听者  
  8.     /* 
  9.      第一个参数:怎么分配存储空间 
  10.      第二个参数:要监听的状态 kCFRunLoopAllActivities 所有的状态 
  11.      第三个参数:时候持续监听 
  12.      第四个参数:优先级 总是传0 
  13.      第五个参数:当状态改变时候的回调 
  14.      */  
  15.     CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {  
  16.   
  17.         /* 
  18.          kCFRunLoopEntry = (1UL << 0),        即将进入runloop 
  19.          kCFRunLoopBeforeTimers = (1UL << 1), 即将处理timer事件 
  20.          kCFRunLoopBeforeSources = (1UL << 2),即将处理source事件 
  21.          kCFRunLoopBeforeWaiting = (1UL << 5),即将进入睡眠 
  22.          kCFRunLoopAfterWaiting = (1UL << 6), 被唤醒 
  23.          kCFRunLoopExit = (1UL << 7),         runloop退出 
  24.          kCFRunLoopAllActivities = 0x0FFFFFFFU 
  25.          */  
  26.         switch (activity) {  
  27.             case kCFRunLoopEntry:  
  28.                 NSLog(@"即将进入runloop");  
  29.                 break;  
  30.             case kCFRunLoopBeforeTimers:  
  31.                 NSLog(@"即将处理timer事件");  
  32.                 break;  
  33.             case kCFRunLoopBeforeSources:  
  34.                 NSLog(@"即将处理source事件");  
  35.                 break;  
  36.             case kCFRunLoopBeforeWaiting:  
  37.                 NSLog(@"即将进入睡眠");  
  38.                 break;  
  39.             case kCFRunLoopAfterWaiting:  
  40.                 NSLog(@"被唤醒");  
  41.                 break;  
  42.             case kCFRunLoopExit:  
  43.                 NSLog(@"runloop退出");  
  44.                 break;  
  45.   
  46.             default:  
  47.                 break;  
  48.         }  
  49.     });  
  50.   
  51.     /* 
  52.      第一个参数:要监听哪个runloop 
  53.      第二个参数:观察者 
  54.      第三个参数:运行模式 
  55.      */  
  56.     CFRunLoopAddObserver(CFRunLoopGetCurrent(),observer, kCFRunLoopDefaultMode);  
  57.   
  58.     //NSDefaultRunLoopMode == kCFRunLoopDefaultMode  
  59.     //NSRunLoopCommonModes == kCFRunLoopCommonModes  
  60. }  
  61.   
  62. - (IBAction)RunloopObserver:(id)sender {  
  63.     NSLog(@"处理点击事件:%s",__func__);  
  64. }  

效果图.png

创建一个常驻线程

复制内容到剪贴板
  1. - (IBAction)createThread:(id)sender {  
  2.   
  3.     //1.创建线程  
  4.     self.thread = [[NSThread alloc]initWithTarget:self selector:@selector(task) object:nil];  
  5.     [self.thread start];  
  6. }  
  7. -(void)task  
  8. {  
  9.     NSLog(@"task---%@",[NSThread currentThread]);  
  10.     //    while (1) {  
  11.     //       NSLog(@"task1---%@",[NSThread currentThread]);  
  12.     //    }  
  13.     //解决方法:开runloop  
  14.     //1.获得子线程对应的runloop  
  15.     NSRunLoop *runloop = [NSRunLoop currentRunLoop];  
  16.   
  17.     //保证runloop不退出  
  18.     //NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];  
  19.     //[runloop addTimer:timer forMode:NSDefaultRunLoopMode];  
  20.     [runloop addPort:[NSPort port] forMode:NSDefaultRunLoopMode];  
  21.   
  22.     //2.默认是没有开启  
  23.     [runloop run];//开启  
  24. //    [runloop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:10]];//开启Runloop,到特定时间结束  
  25.   
  26.     NSLog(@"---end----");  
  27. }  
  28. - (IBAction)doOthertask:(id)sender {  
  29.     //[self.thread start];  
  30.   
  31.     [self performSelector:@selector(OtheTask) onThread:self.thread withObject:nil waitUntilDone:YES];  
  32. }  
  33. -(void)OtheTask  
  34. {  
  35.     NSLog(@"OtheTask---%@",[NSThread currentThread]);  
  36. }  
  37.   
  38. -(void)run  
  39. {  
  40.     NSLog(@"%s",__func__);  
  41. }  
  42.   
  43. //Runloop中自动释放池的创建和释放  
  44. //第一次创建:启动runloop  
  45. //最后一次销毁:runloop退出的时候  
  46. //其他时候的创建和销毁:当runloop即将睡眠的时候销毁之前的释放池,重新创建一个新的  

效果图

效果图.png

Runloop源码分析

Runloop源码分析.png

感谢 Cehae-WDong 支持 磐实编程网 原文地址:
blog.csdn.net/cehae/article/details/52773592

文章信息

发布时间:2016-10-10

作者:Cehae-WDong

发布者:aquwcw

浏览次数: