runloop

作者: QYCD | 来源:发表于2021-11-16 15:06 被阅读0次
    什么是runloop

    顾名思义,runloop就是在"跑圈",本质是一个do-while循环。runloop提供了这么一个机制,当有任务处理时,线程的runloop会保持忙碌,而在没有任何任务处理时,会让线程休眠,从而让出CPU,当再次有任务需要处理时,runloop会被唤醒来处理事件,直到任务处理完毕,再次进入休眠。

    • 保持程序的持续运行
    • 处理App中的各种事件(比如触摸事件、定时器事件、Selector事件)
    • 节省CPU资源,提高程序性能: 该做事时做事,该休息时休息
    runloop与线程
    • 一条线程对应一个runloop对象,每条线程都有唯一一个与之对应的runloop对象
    • runloop保存在一个全局的dictionary中,线程作为key,runloop作为value(key: 线程,value: runloop)
    • runloop对象在第一次获取runloop时创建,销毁则是在线程结束的时候
    • 主线程的runloop对象系统自动帮我们建好了,而子线程的runloop对象需要我们主动创建和维护
    • runloop在第一次获取时创建,在线程结束时销毁

    在viewDidLoad方法内(即在主线程中)添加这么一句

    [self performSelector:@selector(testAction) withObject:nil afterDelay:2];
    
    - (void)testAction {
        NSLog(@"==%d", [NSThread isMainThread]);
        NSLog(@"testAction");
    }
    
    执行后,2秒后打印:
    ==1
    testAction
    

    而如果在子线程中执行,则会发现2秒后并没有打印;performSelector:afterDelay:方法实质会创建一个定时器添加到runloop中,虽然每个线程都有一个runloop,但是只有主线程的runloop是开启的,而子线程的runloop需要我们自己run起来,定时器才会执行

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
             // 不执行
            [self performSelector:@selector(testAction) withObject:@3 afterDelay:2];
        });
    

    修改为:

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            [self performSelector:@selector(testAction) withObject:@3 afterDelay:2];
            [[NSRunLoop currentRunLoop] run];
        });
    执行打印:
    ==0
    testAction
    

    app程序,函数入口main函数。
    UIApplicationMain函数内部帮我们开启了主线程的runloop,内部拥有一个无限循环的代码,这样UIApplicationMain函数就不会立刻返回,只要程序不退出/崩溃,就一直循环。

    #import <UIKit/UIKit.h>
    #import "AppDelegate.h"
    
    int main(int argc, char * argv[]) {
        NSString * appDelegateClassName;
        @autoreleasepool {
            // Setup code that might create autoreleased objects goes here.
            appDelegateClassName = NSStringFromClass([AppDelegate class]);
        }
        return UIApplicationMain(argc, argv, nil, appDelegateClassName);
    }
    
    runloop五种模式

    一个 RunLoop 在某个 mode 下运行时,不会接收和处理其他 mode 的事件

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

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

    • UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用,会切换到kCFRunLoopDefaultMode

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

    • kCFRunLoopCommonModes: 这是一个占位用的Mode,作为标记kCFRunLoopDefaultMode和UITrackingRunLoopMode用,并不是一种真正的Mode

    应用场景
    • 可以控制定时器在特定模式下执行--保证NSTimer在有视图滑动时依然能正常执行

    创建定时器的两种方式,在主线程,第一种方式会自动将定时器添加到当前runloop并且执行(run)的;第二种需要我们手动添加到当前runloop

    // 方式1 默认将定时器添加到NSDefaultRunLoopMode模式
        NSTimer *timer1 = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(run) userInfo:nil repeats:YES];
        [timer1 fire]; //立即执行
        
        // 方式2
        NSTimer *timer2 = [NSTimer timerWithTimeInterval:2 target:self selector:@selector(run) userInfo:nil repeats:YES];
        [[NSRunLoop currentRunLoop] addTimer:timer2 forMode:NSDefaultRunLoopMode];
        [timer2 fire];
    

    当页面滑动时当前runloop的mode从NSDefaultRunLoopMode切换到UITrackingRunLoopMode,此时原先的NSDefaultRunLoopMode事件将不再处理,不再滑动时再次切换会NSDefaultRunLoopMode后,定时器事件才继续执行;如果创建定时器时定义NSRunLoopCommonModes模式,则即使页面滑动也不会影响定时器事件的执行

    • UIImageView延迟加载图片
      使用performSelector: withObject: afterDelay: inModes:方法将imageView图片的加载放到NSDefaultRunLoopMode,保证滑动视图的流畅性

    • 开启一个常驻线程

    iOS RunLoop总结

    相关文章

      网友评论

          本文标题:runloop

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