RunLoop

作者: coma | 来源:发表于2015-11-14 13:43 被阅读336次

RunLoop介绍

  • Run loop 本身听起来就和它的名字很像。它是一个循环,你的线程进入并使用它来运行响应输入事件的事件处理程序。你的代码要提供实现循环部分的控制语句,换言之就是要有 while 或 for 循环语句来驱动 run loop。在你的循环中,使用 run loopobject 来运行事件处理代码,它响应接收到的事件并启动已经安装的处理程序。节省CPU资源,提高程序性能:该做事时做事,该休息时休息
RunLoop 处理逻辑.png
  • RunLoop对象
    iOS中有2套API来访问和使用RunLoop
    Foundation ->NSRunLoop
    Core Foundation ->CFRunLoopRef
    NSRunLoop和CFRunLoopRef都代表着RunLoop对象
    NSRunLoop是基于CFRunLoopRef的一层OC包装,所以要了解RunLoop内部结构,需要多研究CFRunLoopRef层面的API(Core Foundation层面)

  • RunLoop与线程
    1.一条线程对应一个RunLoop
    2.RunLoop在第一次获取时创建,在线程结束时销毁
    3.主线程的RunLoop默认已经创建好了, 而子线程的需要我们自己手动创建
    4.一个NSRunLoop/CFRunLoopRef, 就代表一个RunLoop对象
    5.只要线程结束了, 那么与之对应的RunLoop对象也会被释放
    每条线程都有唯一的一个与之对应的RunLoop对象
    6.如何获取主线程对应的RunLoop对象,
    mainRunLoop/CFRunLoopGetMain
    7.如何获取当前线程对应的RunLoop对象,

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

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

RunLoop相关类

  • Core Foundation中关于RunLoop的5个类
    1.CFRunLoopRef
    2.CFRunLoopModeRef
    3.CFRunLoopSourceRef
    4.CFRunLoopTimerRef
    5.CFRunLoopObserverRef
RunLoop.png
  • NSRunLoopMode中的类
  • CFRunLoopModeRef代表RunLoop的运行模式,一个 RunLoop 包含若干个 Mode,每个Mode又包含若干个Source/Timer/Observer,每次RunLoop启动时,只能指定其中一个 Mode,这个Mode被称作 CurrentMode,如果需要切换Mode,只能退出Loop,再重新指定一个Mode进入,这样做主要是为了分隔开不同组的Source/Timer/Observer,让其互不影响
  • 系统默认注册了5个Mode:
    1.NSDefaultRunLoopMode:App的默认Mode,通常主线程是在这个Mode下运行
    2.UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
    3.UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用
    4.GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到
    5.NSRunLoopCommonModes: 这是一个占位用的Mode,不是一种真正的Mode
  • NSRunLoopMode的应用
 //1.创建一个NSTimer
     NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(show) userInfo:nil repeats:YES];
 // 2.将NSTimer添加到RunLoop中, 并且告诉系统, 当前Tiemr只有在RunLoop的默认模式下才有效
     [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
     
// 2.将NSTimer添加到RunLoop中, 并且告诉系统, 当前Tiemr只有在Tracking的默认模式下才有效
//   [[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
     
// 2.将NSTimer添加到RunLoop中, 并且告诉系统, 在所有被"标记"common的模式都可以运行,  
     UITrackingRunLoopMode和kCFRunLoopDefaultMode都被标记为了common模式, 所以只需要  
     将timer的模式设置为forMode:NSRunLoopCommonModes,就可以在默认模式和追踪模式都能够
     运行
     //    [[NSRunLoop currentRunLoop]   addTimer:timer forMode:NSRunLoopCommonModes];
 // 注意: 如果是通过scheduledTimerWithTimeInterval创建的NSTimer, 默认就会添加到
RunLoop得DefaultMode中 , 所以它会自动运行
    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(show) userInfo:nil repeats:YES];
// 虽然默认已经添加到DefaultMode中, 但是我们也可以自己修改它的模式
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
- (void)show
{
    NSLog(@"%s", __func__);
}
  • CFRunLoopObserverRef
    CFRunLoopObserverRef是观察者,能够监听RunLoop的状态改变
  // 创建Observer
    /*
     第1个参数: 指定如何给observer分配存储空间
     第2个参数: 需要监听的状态类型/ kCFRunLoopAllActivities监听所有状态
     第3个参数: 是否每次都需要监听
     第4个参数: 优先级
     第5个参数: 监听到状态改变之后的回调
     */
    CFRunLoopObserverRef  observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        switch (activity) {
            case kCFRunLoopEntry:
                NSLog(@"即将进入runloop");
                break;
            case kCFRunLoopBeforeTimers:
                NSLog(@"即将处理timer");
                break;
            case kCFRunLoopBeforeSources:
                NSLog(@"即将处理source");
                break;
            case kCFRunLoopBeforeWaiting:
                NSLog(@"即将进入睡眠");
                break;
            case kCFRunLoopAfterWaiting:
                NSLog(@"刚从睡眠中唤醒");
                break;
            case kCFRunLoopExit:
                NSLog(@"即将退出");
                break;
            default:
                break;
        }
    });
    
    
    // 给主线程的RunLoop添加一个观察者
    /*
     第1个参数: 需要给哪个RunLoop添加观察者
     第2个参数: 需要添加的Observer对象
     第3个参数: 在哪种模式下可以可以监听
     */
    CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopDefaultMode);
    
    // 释放对象
凡是带有Create、Copy、Retain等字眼的函数,创建出来的对象,都需要在最后做一次release
比如CFRunLoopObserverCreate
release函数:CFRelease(对象);
    CFRelease(observer);
    
    [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(show) userInfo:nil repeats:YES];
- (void)show{
    
}
  • CFRunLoopSourceRef
    CFRunLoopSourceRef是事件源(输入源)

  • 按照官方文档,Source的分类
    Port-Based Sources
    Custom Input Sources
    Cocoa Perform Selector Sources

  • 按照函数调用栈,Source的分类
    Source0:非基于Port的, 用于用户主动触发事件
    Source1:基于Port的,通过内核和其他线程相互发送消息

  • CFRunLoopTimerRef
    CFRunLoopTimerRef是基于时间的触发器
    CFRunLoopTimerRef基本上说的就是NSTimer,它受RunLoop的Mode影响
    GCD的定时器不受RunLoop的Mode影响

RunLoop应用场景

- (void)viewDidLoad
{
     //自动释放池什么时候创建和释放
     // 1.第一次创建, 是在runloop进入的时候创建  对应的状态 = kCFRunLoopEntry
     //2.最后一次释放, 是在runloop退出的时候  对应的装 = kCFRunLoopExit
     //3.其它创建和释放
     //每次睡觉的时候都会释放前自动释放池, 然后再创建一个新的
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(show) object:nil];
    self.thread = thread;
    [thread start];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    NSLog(@"%s", __func__);
    /*
    // 1.在指定模式下进行特定的操作
    [self.imageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@"abc"] afterDelay:2.0 inModes:@[UITrackingRunLoopMode]];
     */
    
    // 默认清空下一个线程只能使用一次, 也就是说只能执行一个操作, 执行完毕之后就不能使用了
    [self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:YES];
}
- (void)show{
    NSLog(@"%s", __func__);  
    // 1.子线程的NSRunLoop需要手动创建
    // 2.子线程的NSRunLoop需要手动开启
    // 3.如果子线程的NSRunLoop没有设置source or timer, 那么子线程的NSRunLoop会立刻关闭
    //相当于添加了一个基于端口的source
//    [[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
   
//    NSTimer *timer = [NSTimer timerWithTimeInterval:5.0 target:self selector:@selector(test) userInfo:nil repeats:YES];
//    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
//    [[NSRunLoop currentRunLoop] run];
// 注意点: NSRunLoop只会检查有没有source和timer, 没有就关闭, 不会检查observer
    CFRunLoopObserverRef  observer = 
CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
    });

    CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
    // 释放对象
    CFRelease(observer);
    
    [[NSRunLoop currentRunLoop] run];
    NSLog(@"end -----");
}

- (void)test
{
    NSLog(@"%s %@", __func__, [NSThread currentThread]);
}

相关文章

网友评论

      本文标题:RunLoop

      本文链接:https://www.haomeiwen.com/subject/yczhhttx.html