RunLoop相关

RunLoop表示 运行循环

基本作用:

  • 保存程序的持续运行
  • 处理程序中的各种事件(触摸事件、定时器事件、Selector等)
  • 节省CPU的资源,提高程序的性能,该做事的时候做事,该休息的时候休息

RunLoop其实是一个死循环,永远都不会结束,由于main函数中就启动了一个RunLoop,这样才能保证程序的持续运行不会退出

RunLoop默认是和主线程相关联的

RunLoop对象:

  • iOS中有两套API来访问和使用RunLoop
  • Foundation,通过NSRunLoop获得
  • Core Foundation,通过CFRunLoopRef获得

NSRunLoop和CFRunLoopRef都代表着RunLoop对象

NSRunLoop基于CFRunLoopRef的一层OC包装,要了解NSRunLoop结构需要多了解CFRunLoopRef层面的API(Core Foundation层面)

RunLoop与线程

  • 每一条线程都有一个唯一的与之对应的RunLoop对象
  • 主线程的RunLoop已经自动创建好了,子线程的RunLoop需要主动创建
  • RunLoop在第一次获取时创建,在线程结束时销毁

NSRunLoop获得RunLoop

[NSRunLoop currentRunLoop]; //获得当前RunLoop
[NSRunLoop mainRunLoop];//获得主线程RunLoop

CFRunLoopRef获得RunLoop

CFRunLoopGetMain();
CFRunLoopGetCurrent();

从NSRunLoop对象中获取CFRunLoopRef对象

NSRunLoop *nsRunLoop = [NSRunLoop currentRunLoop];
CFRunLoopRef cfRunLoop = [nsRunLoop getCFRunLoop];

创建RunLoop

- (void)newThread
{
    [[[NSThread alloc]initWithTarget:self selector:@selector(run) object:nil] start];
}

- (void) run
{
    //[NSRunLoop currentRunLoop]可以表示当前的RunLoop,也可以创建一个RunLoop,因为它是懒加载模式
    NSLog(@"%@",[NSRunLoop currentRunLoop]);
}

RunLoop相关类

Core Foundation中的5个类
  • CFRunLoopRef
  • CFRunLoopModeRef //RunLoop模式
  • CFRunLoopSourceRef //输入源
  • CFRunLoopTimerRef //定时器
  • CGRunLoopObserverRef //观察者

以上五个类的图形表示关系:

在RunLoop中有多个运行模式,但是RunLoop只能选择一种模式

mode里至少要有一个timer或是source

CFRunLoopModeRef
  • CFRunLoopModeRef代表RunLoop的运行模式
  • 一个RunLoop包含多个Mode,每个Mode又包含若干个Source / Timer / Observer
  • 每次RunLoop启动时,只能指定其中一种Mode,这个Mode称作CurrentMode
  • 如果需要切换Mode,只能退出Loop,再重新指定一个Mode进入
  • 这样做主要是为了分隔不同组的Source / Timer / Observer,互不影响

系统默认注册了5个Mode:

  • kCFRunLoopDefoultMode: App默认Mode,通常主线程在这个Mode下运行
  • UITrackingRunLoopMode: 界面跟踪Mode,用于ScrollView追踪触摸滑动,保证界面滑动时不受其他Mode影响
  • UIInitializationRunLoopMode 在刚启动App时进入的第一个Mode,启动完成后就不再使用
  • GSEventReceiveRunLoopMode 接受系统事件的内部Mode,通常用不到
  • kCFRunLoopCommonModes:是一个占位用的Mode,不是一种真正意义的Mode

CFRunLoopTimerRef

添加定时器,模式为NSDefaultRunLoopMode

- (void) timer
{
    NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];

    /*
     * 参数1 添加一个定时器
     * 参数2 runloop的运行模式
     */
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
}

- (void) run
{
    NSLog(@"run-----%@------%@",[NSThread currentThread],[NSRunLoop currentRunLoop].currentMode);
}

如果界面上有一个ScrollView,当用户操作的时候,Timer就不起作用了,因为RunLoop运行模式自动切换到UITrackingRunLoopMode界面追踪模式,当用户停止操作后,Timer又开始工作了,RunLoop运行模式自动切换回kCFRunLoopDefoultMode

如果上述代码要在ScrollView滚动的时候定时器才起作用,需要修改代码:

//修改 [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; 为:
[[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];

如果需要无论ScrollView是否在滚动定时器都起作用,需要修改代码:

笨办法:

[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];

[[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];

其实正确的办法:

//NSRunLoopCommonModes = NSDefaultRunLoopMode + UITrackingRunLoopMode
//占用,标签,凡事添加到NSRunLoopCommonModes中的事件都会同时添加到打上common标签的运行模式
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

另外

[NSTimer scheduledTimerWithTimeInterval:(NSTimeInterval) target:(nonnull id) selector:(nonnull SEL) userInfo:(nullable id) repeats:(BOOL)];

这个方法则不需要创建RunLoop也可以工作,因为内部已经做了处理,但这个方法不能在子线程工作

CFRunLoopSourceRef

CFRunLoopSourceRef 是事件源(输入源)

以前的分类方式:

  • Port-Based Sources 基于端口的事件源
  • Custom Input Sources 自定义输入源
  • Cocoa Perform Selector Sources

现在的分类方式:(分类的依据是函数调用栈)

  • Source0:非基于Port(端口),由用户创建的
  • Source1:基于Port(端口),由系统内部消息事件

CFRunLoopObserverRef

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

可以监听的事件点有以下几个:

- (void)observer
{
    //先手动创建一个监听着



    /*
     * 参数1:怎么分配存储空间
     * 参数2:观察者
     * 参数3:时候持续监听
     * 参数4:优先级,总是传0
     * 参数5:当状态改变时会调用的Block
     */

    //CFRunLoopObserverRef observer = CFRunLoopObserverCreate(CFAllocatorRef allocator, CFOptionFlags activities, Boolean repeats, CFIndex order, CFRunLoopObserverCallBack callout, CFRunLoopObserverContext *context);

    CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
       /*
        /* Run Loop Observer Activities */
        typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
            kCFRunLoopEntry = (1UL << 0), //即将进入Loop
            kCFRunLoopBeforeTimers = (1UL << 1), //即将处理Timer
            kCFRunLoopBeforeSources = (1UL << 2),//即将处理Source
            kCFRunLoopBeforeWaiting = (1UL << 5),//即将进入休眠
            kCFRunLoopAfterWaiting = (1UL << 6),//刚从休眠中唤醒
            kCFRunLoopExit = (1UL << 7),//即将退出Loop
            kCFRunLoopAllActivities = 0x0FFFFFFFU
        };
        */
       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(@"即将退出Loop");
               break;

           default:
               break;
       }
   });

    /*
     * 参数1:CFRunLoopRef rl 要监听那个RunLoop
     * 参数2:CFRunLoopObserverRef observer 观察者
     * 参数3:CFRunLoopMode mode 运行模式
     */
    CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
    //以下枚举等价,只不过一个是OC的,一个是C的
    //NSDefaultRunLoopMode == kCFRunLoopDefaultMode
    //NSRunLoopCommonModes == kCFRunLoopCommonModes
}

results matching ""

    No results matching ""