美文网首页
定时器、RunLoop、多线程

定时器、RunLoop、多线程

作者: huoshe2019 | 来源:发表于2019-10-08 10:59 被阅读0次

    前言

    这里主要分析定时器与RunLoop、多线程之间的关系以及易错点。
    定时器包含以下几部分:

    • NSTimer
    • CADisplayLink
    • GCD
    • performSelector:afterDelay

    小注:

    • 上面的定时器除了GCD,其它都是基于RunLoop的,也就是说如果在子线程(默认不开启RunLoop),所有定时器方法都不会执行的。
    • GCD定时器精确到纳秒
    • GCD并不运行在RunLoop中
    • RunLoop底层采用GCD定时器

    一、NSTimer

    1.1、自动加入当前的RunLoop、模式是default mode。

    但是子线程中,需要手动创建runloop,并进行创建

    • scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
    • scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo;
    • scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block;

    1.2、不会自动加入RunLoop、需要手动addTimer:forMode

    • timerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block;
    • timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
    • timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo;
    • initWithFireDate:(NSDate *)date interval:(NSTimeInterval)ti target:(id)t selector:(SEL)s userInfo:(id)ui repeats:(BOOL)rep;
    • initWithFireDate:(NSDate *)date interval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block;

    易错题:

        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            //不会执行
            [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerUpdate) userInfo:nil repeats:YES];
            //添加这句、可以正常执行
            //必须在下面,因为不添加Source,不会正常开启RunLoop
            [[NSRunLoop currentRunLoop] run];
        });
    

    二、CADisplayLink

    这里需要手动加入RunLoop

    // 创建CADisplayLink
    CADisplayLink *disLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(linkMethod)];
    //触发间隔
    disLink.frameInterval = 2;
    // 添加至RunLoop中
    [disLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
    // 终止定时器
    [disLink invalidate];
    // 销毁对象
    disLink = nil;
    

    三、GCD

    /** 创建定时器对象
     * para1: DISPATCH_SOURCE_TYPE_TIMER 为定时器类型
     * para2-3: 中间两个参数对定时器无用
     * para4: 最后为在什么调度队列中使用
     */
    _gcdTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));
    /** 设置定时器
     * para2: 任务开始时间
     * para3: 任务的间隔
     * para4: 可接受的误差时间,设置0即不允许出现误差
     * Tips: 单位均为纳秒
     */
    dispatch_source_set_timer(_gcdTimer, DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC, 0.0 * NSEC_PER_SEC);
    /** 设置定时器任务
     * 可以通过block方式
     * 也可以通过C函数方式
     */
    dispatch_source_set_event_handler(_gcdTimer, ^{
        static int gcdIdx = 0;
        NSLog(@"GCD Method: %d", gcdIdx++);
        NSLog(@"%@", [NSThread currentThread]);
        
        if(gcdIdx == 5) {
            // 终止定时器
            dispatch_suspend(_gcdTimer);
        }
    });
    // 启动任务,GCD计时器创建后需要手动启动
    dispatch_resume(_gcdTimer);
    

    备注:
    GCD不依赖RunLoop

    四、performSelector:afterDelay

    4.1、概念

    • 是消息传递的一种方式
    • 不需要编译的时候声明这些方法,是运行时

    4.2、相关方法

    4.2.1、同步执行、在任何线程正常执行(不受RunLoop影响)

    • (id)performSelector:(SEL)aSelector;
    • (id)performSelector:(SEL)aSelector withObject:(id)object;
    • (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;

    4.2.2、异步执行、只在主线程执行(RunLoop影响)

    原因就是子线程RunLoop默认不开启,定时器必须加入RunLoop才能正常执行。

    • (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray * )modes;
    • (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay;

    4.2.3、主线程(waitUntilDone决定是否阻塞主线程)(不受RunLoop影响)

    • (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray * )array;
    • (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;

    4.2.4、主线程和子线程(waitUntilDone决定是否阻塞主线程)(不受RunLoop影响)

    • (void)performSelector:(SEL)aSelector onThread:(NSThread * )thr withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray * )array;
    • (void)performSelector:(SEL)aSelector onThread:(NSThread * )thr withObject:(id)arg waitUntilDone:(BOOL)wait;

    总结:

    1、不受Runloop、子线程影响,可以正常使用的
    GCD
    2、不受Runloop、受子线程影响(需要手动启动Runloop)
    NSTimer➡️scheduledTimer...方法
    3、受Runloop、受子线程影响(需要手动加入Runloop、需要手动启动)
    NSTimer➡️除了scheduledTimer...方法
    CADisplayLink
    performSelector: afterDelay:

    相关文章

      网友评论

          本文标题:定时器、RunLoop、多线程

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