Runloop(一) 基本概念

459 阅读3分钟

1.概述

什么是Runloop

如果没有Runloop

Runloop与线程

  • 每条线程都有唯一与之对应的Runloop对象
  • Runloop保存在一个全局的Dictionary里,线程作为key,Runloop作为value.
  • 线程刚创建时并没有Runloop对象,Runloop会在第一次获取它时创建
  • Runloop会在线程结束时销毁
  • 主线程的Runloop已经自动获取(创建),子线程默认没有开启Runloop。
//    NSRunLoop *runloop;
//    CFRunLoopRef runloop2;
    
//    runloops[thread] = runloop; //保存在全局字典里,线程为key
    
//    NSRunLoop *runloop = [NSRunLoop currentRunLoop];
//    CFRunLoopRef runloop2 = CFRunLoopGetCurrent();
    
//    NSArray *array;
//    CFArrayRef arry2;
//
//    NSString *string;
//    CFStringRef string2;
    
    NSLog(@"%p %p", [NSRunLoop currentRunLoop], [NSRunLoop mainRunLoop]);
    NSLog(@"%p %p", CFRunLoopGetCurrent(), CFRunLoopGetMain());

  • CFRunLoopGetCurrent()[NSRunLoop currentRunLoop]地址不一样。

Runloop模式

  • CFRunLoopModeRef代表Runloop的运行模式
  • 一个Runloop包含若干个Mode,每个Mode又包含若干个Source0/Source1/Timer/Observer
  • Runloop启动时只能选择其中一个Mode,作为currentMode
  • 如果需要切换Mode,只能退出当前Loop,再重新选择一个Mode进入
    • 不同组的Source0/Source1/Timer/Observer能分割开来,互不影响
  • 如果Mode里没有任何Source0/Source1/Timer/Observer,Runloop会立马退出

常见的2种Mode

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

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

  • 切换模式并不会导致Runloop退出。

Runloop运行模式

  • 可以看出触摸事件是通过Source0处理,然后调用__handleEventQueueInternal,再调用__handleEventQueueInternal

  • 系统事件是由Source1捕捉,然后交给Source0处理。

  • Observers

    • UI刷新,线程睡觉前刷新UI.线程没什么事干就睡觉,self.view.backColor = [uicolor redcolor];不是一执行这个代码就生效,会先记住这个事件。通过observe监听线程状态,监听到要睡觉前刷新UI。
    • Autorelease pool 线程睡觉前,清理需要释放的对象。
    • BeforeWaiting 休眠前
    • AfterWaiting 唤醒
  • 程序所有代码,都是由Runloop决定什么时候去执行。

void observeRunLoopActicities(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
{
    switch (activity) {
        case kCFRunLoopEntry:
            NSLog(@"kCFRunLoopEntry");
            break;
        case kCFRunLoopBeforeTimers:
            NSLog(@"kCFRunLoopBeforeTimers");
            break;
        case kCFRunLoopBeforeSources:
            NSLog(@"kCFRunLoopBeforeSources");
            break;
        case kCFRunLoopBeforeWaiting:
            NSLog(@"kCFRunLoopBeforeWaiting");
            break;
        case kCFRunLoopAfterWaiting:
            NSLog(@"kCFRunLoopAfterWaiting");
            break;
        case kCFRunLoopExit:
            NSLog(@"kCFRunLoopExit");
            break;
        default:
            break;
    }
}
// 创建Observer
    // 第二个参数:监听哪种activity,这里是监听全部
    // 第三个参数:是否重复监听
    // 第四个参数: 监听顺序。这里不按照顺序监听
    // 第五个参数需传进一个C语言方法
    // 第六个参数则是第五个参数方法的info参数
    CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, observeRunLoopActicities, NULL);
    
    // 添加Observer到RunLoop中
    //kCFRunLoopCommonModes默认包括kCFRunLoopDefaultMode、UITrackingRunLoopMode
    CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);
    // 释放
    CFRelease(observer);
    
    // 另一种方法创建observer
    // 创建Observer
//    CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
//        switch (activity) {
//            case kCFRunLoopEntry: {
//                CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
//                NSLog(@"kCFRunLoopEntry - %@", mode);
//                CFRelease(mode);
//                break;
//            }
//
//            case kCFRunLoopExit: {
//                CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
//                NSLog(@"kCFRunLoopExit - %@", mode);
//                CFRelease(mode);
//                break;
//            }
//
//            default:
//                break;
//        }
//    });
//    // 添加Observer到RunLoop中
//    CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);
//    // 释放
//    CFRelease(observer);
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
        //观察source
//   NSLog(@"%s",__func__);
    
    
    // 观察NSTimer
    [NSTimer scheduledTimerWithTimeInterval:3.0 repeats:NO block:^(NSTimer * _Nonnull timer) {
        NSLog(@"定时器-----------");
    }];
    
}

观察滚动页面,ViewController里加一个UITextView,用下面代码监听:

// 另一种方法创建observer
    // 创建Observer
    CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        switch (activity) {
            case kCFRunLoopEntry: {
                CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
                NSLog(@"kCFRunLoopEntry - %@", mode);
                CFRelease(mode);  //copy出来的需要释放
                break;
            }

            case kCFRunLoopExit: {
                CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
                NSLog(@"kCFRunLoopExit - %@", mode);
                CFRelease(mode);
                break;
            }

            default:
                break;
        }
    });
    // 添加Observer到RunLoop中
    CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);
    // 释放
    CFRelease(observer);