定时器、RunLoop、多线程

2019-10-08  本文已影响0人  huoshe2019

前言

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

小注:

一、NSTimer

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

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

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

易错题:

    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:

上一篇下一篇

猜你喜欢

热点阅读