美文网首页
iOS runloop 简述及实例

iOS runloop 简述及实例

作者: 依米米一 | 来源:发表于2020-05-10 18:29 被阅读0次

    一、概念

    runloop
    1、字面理解:“跑圈”“循环执行”
    2、实义:通过内部维护的事件循环来对事件/消息进行管理的一个对象
    ** 寄生于线程的消息循环机制,保证线程的存活,而不是线程执行完任务后就消亡
    ** 没有消息处理->休眠以避免资源占用(用户态->内核态)
    ** 有消息处理->立即唤醒(内核态->用户态)

    二、整体结构

    1.一个runloop对应了多种mode,每个mode下又有多种source,timer,Observer
    2.每次RunLoop启动时,只能指定其中一个 Mode,这个Mode被称作 CurrentMode
    3.如果需要切换Mode,只能退出Loop,再重新指定一个Mode进入,这样做主要是为了分隔开不同组的Source/Timer/Observer,让其互不影响


    整体图

    三、Runloop运行逻辑

    事件队列

    事件循环的实现机制是
    当你被问到,runloop具体操作流程的时候,你该怎么回答。
    当我们手动点击屏幕,UI操作,实际runloop是这样来的


    事件循环的实现机制

    四、主线程默认开启runloop,子线程是不开启RunLoop的!!!子线程的代码执行完毕后就会被回收!!!上代码

    @interface ViewController ()
    @property (nonatomic,assign,getter=isfinish) BOOL finish;
    @end
    
    @implementation ViewController
    - (void)viewDidLoad {
        [super viewDidLoad];
        _finish = YES;
        //子线程
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            //初始化NSTimer
            NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timeup) userInfo:nil repeats:YES];
            [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
            
            while (self.finish) {
                //开启运行循环
                [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceReferenceDate:0.1]];
            }
            NSLog(@"当RunLoop停止的时候线程会被销毁");
            NSLog(@"%@",[NSThread currentThread]);
        });
    }
    - (void)timeup {
        NSLog(@"timer%@",[NSThread currentThread]);
    }
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        _finish = NO;
    }
    

    运行结果 可正常执行子线程

    2020-05-09 17:36:58.740731+0800 demoTest[86561:2515635] timer<NSThread: 0x600002137400>{number = 3, name = (null)}
    2020-05-09 17:36:59.744646+0800 demoTest[86561:2515635] timer<NSThread: 0x600002137400>{number = 3, name = (null)}
    2020-05-09 17:37:00.743766+0800 demoTest[86561:2515635] timer<NSThread: 0x600002137400>{number = 3, name = (null)}
    2020-05-09 17:37:01.743925+0800 demoTest[86561:2515635] timer<NSThread: 0x600002137400>{number = 3, name = (null)}
    2020-05-09 17:37:02.740088+0800 demoTest[86561:2515635] timer<NSThread: 0x600002137400>{number = 3, name = (null)}
    

    点击屏幕任何地方后运行结果:线程被销毁,即不再执行运行循环

     //开启运行循环
     [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceReferenceDate:0.1]];
    
    2020-05-09 17:36:59.744646+0800 demoTest[86561:2515635] timer<NSThread: 0x600002137400>{number = 3, name = (null)}
    2020-05-09 17:37:00.743766+0800 demoTest[86561:2515635] timer<NSThread: 0x600002137400>{number = 3, name = (null)}
    2020-05-09 17:37:01.743925+0800 demoTest[86561:2515635] timer<NSThread: 0x600002137400>{number = 3, name = (null)}
    2020-05-09 17:37:02.740088+0800 demoTest[86561:2515635] timer<NSThread: 0x600002137400>{number = 3, name = (null)}
    2020-05-09 17:37:03.189652+0800 demoTest[86561:2515635] 当RunLoop停止的时候线程会被销毁
    2020-05-09 17:37:03.189834+0800 demoTest[86561:2515635] <NSThread: 0x600002137400>{number = 3, name = (null)}
    
    小结:主线程默认开启runloop,子线程不开启RunLoop,需要代码添加执行循环

    五、实现一个常驻线程

    如何实现一个常驻线程,又怎么销毁一个常驻线程。
    说明:为什么要实现常驻线程,因为有些操作,需要多次在子线程中执行,但是我们不想每次都开启执行一次,结束了。。多处使用场景,让子线程runloop一直存在,节省内存开销。

    三种方法实现常驻线程
    创建子线程

    @interface ViewController ()
    @property(nonatomic,strong)NSThread * testThread;
    @property(nonatomic,assign)BOOL runAlways;
    @end
    
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.testThread = [[NSThread alloc] initWithTarget:self selector:@selector(runTask) object:nil];
        self.testThread.name = @"测试线程";
        [self.testThread start];
    }
    

    实现常驻线程

    1、addPort
    2、addTimer
    3、CFRunLoopAddSource

    -(void)runTask{
        self.runAlways = YES;
        NSLog(@"执行线程:%@",[NSThread currentThread]);
    
        //--------第一种 addPort
        //获取当前线程的runLoop
        NSRunLoop* runLoop = [NSRunLoop currentRunLoop];
        //为当前的runLoop添加model
        [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
        //运行 runLoop
        [runLoop run];
    
    
        //----------第二种 addTimer
        /*
        //获取当前线程的runLoop
        NSRunLoop* runLoop = [NSRunLoop currentRunLoop];
        NSTimer * timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(runTask2) userInfo:nil repeats:YES];
        [runLoop addTimer:timer forMode:NSRunLoopCommonModes];
        [runLoop run];
        */
    
    
      //----------第三种source
        /*
        //创建source
        CFRunLoopSourceContext context = {0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
        CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
        //创建runloop同时在DefaultMode模式下添加source
        CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
        while (self.runAlways) {
            NSLog(@"source--当前线程:%@",[NSThread currentThread]);
            CFRunLoopRun();
        }
        //某一时机 runAlways为no时,保证跳出runloop,线程退出
        CFRunLoopRemoveSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
        CFRelease(source);
    */
    
    }
    
    -(void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        [self performSelector:@selector(runTask2) onThread:self.testThread withObject:nil waitUntilDone:NO];
    }
    
    -(void)runTask2{
        NSLog(@"当前线程:%@",[NSThread currentThread]);
        NSLog(@"%s 完成",__PRETTY_FUNCTION__);
    }
    

    1、addPort 运行结果 实现常驻,触发页面时持有线程

    2020-05-10 11:18:04.837590+0800 demoOne[33638:1500106] 执行线程:<NSThread: 0x600001e8b540>{number = 5, name = 测试线程}
    2020-05-10 11:18:06.616884+0800 demoOne[33638:1500106] 当前线程:<NSThread: 0x600001e8b540>{number = 5, name = 测试线程}
    2020-05-10 11:18:06.617043+0800 demoOne[33638:1500106] -[ViewController runTask2] 完成
    

    2、addTimer 运行结果 实现常驻,每隔1.0秒走一次方法

    2020-05-10 11:28:00.349596+0800 demoOne[33668:1505508] 执行线程:<NSThread: 0x60000089ec00>{number = 6, name = 测试线程}
    2020-05-10 11:28:01.352583+0800 demoOne[33668:1505508] 当前线程:<NSThread: 0x60000089ec00>{number = 6, name = 测试线程}
    2020-05-10 11:28:01.352864+0800 demoOne[33668:1505508] -[ViewController runTask2] 完成
    2020-05-10 11:28:01.436690+0800 demoOne[33668:1505508] 当前线程:<NSThread: 0x60000089ec00>{number = 6, name = 测试线程}
    2020-05-10 11:28:01.436875+0800 demoOne[33668:1505508] -[ViewController runTask2] 完成
    2020-05-10 11:28:02.352970+0800 demoOne[33668:1505508] 当前线程:<NSThread: 0x60000089ec00>{number = 6, name = 测试线程}
    2020-05-10 11:28:02.353353+0800 demoOne[33668:1505508] -[ViewController runTask2] 完成
    2020-05-10 11:28:03.352095+0800 demoOne[33668:1505508] 当前线程:<NSThread: 0x60000089ec00>{number = 6, name = 测试线程}
    2020-05-10 11:28:03.352318+0800 demoOne[33668:1505508] -[ViewController runTask2] 完成
    

    3、CFRunLoopAddSource 运行结果 实现常驻,触发页面时持有线程

    2020-05-10 11:30:03.492318+0800 demoOne[33706:1507948] 执行线程:<NSThread: 0x60000043a840>{number = 7, name = 测试线程}
    2020-05-10 11:30:03.492603+0800 demoOne[33706:1507948] source--当前线程:<NSThread: 0x60000043a840>{number = 7, name = 测试线程}
    2020-05-10 11:30:05.379058+0800 demoOne[33706:1507948] 当前线程:<NSThread: 0x60000043a840>{number = 7, name = 测试线程}
    2020-05-10 11:30:05.379283+0800 demoOne[33706:1507948] -[ViewController runTask2] 完成
    
    source 退出子线程操作 添加限制条件
    
    - (void)createButton{
       UIButton * btn = [UIButton buttonWithType:UIButtonTypeCustom];
        btn.frame = CGRectMake(100, 100, 100, 100);
        btn.backgroundColor = [UIColor lightGrayColor];
        [btn setTitle:@"按钮" forState:UIControlStateNormal];
        [btn addTarget:self action:@selector(click) forControlEvents:UIControlEventTouchUpInside];
        [self.view addSubview:btn];
    
    }
    
    
    - (void)click{
        self.runAlways = NO;
        NSLog(@"点击按钮source移除----当前线程:%@",[NSThread currentThread]);
    }
    
    运行结果:当前线程已在主线程,点击屏幕又会持有子线程
    2020-05-10 11:53:41.415894+0800 demoOne[33924:1525077] 执行线程:<NSThread: 0x600001b57740>{number = 7, name = 测试线程}
    2020-05-10 11:53:43.843947+0800 demoOne[33924:1525077] source--当前线程:<NSThread: 0x600001b57740>{number = 7, name = 测试线程}
    2020-05-10 11:53:46.977248+0800 demoOne[33924:1525077] 当前线程:<NSThread: 0x600001b57740>{number = 7, name = 测试线程}
    2020-05-10 11:53:46.977501+0800 demoOne[33924:1525077] -[ViewController runTask2] 完成
    2020-05-10 11:53:50.492606+0800 demoOne[33924:1524988] 点击按钮source移除----当前线程:<NSThread: 0x600001b18ec0>{number = 1, name = main}
    

    六、具体实例

    CFRunloopSourceRef

    1、Source0:非基于Port的 ,用于用户主动触发事件
    2、Source1:基于Port的,通过内核和其它线程相互发送消息
    可以通过打断点的方式查看一个方法的函数调用栈


    函数调用栈
    AFNetworking 中运用 Runloop

    AFURLConnectionOperation 基于 NSURLConnection 构建,其希望能在后台线程接收 Delegate 回调。为此 AFNetworking 单独创建了一个线程,并在这个线程中启动了一个 RunLoop

    + (void)networkRequestThreadEntryPoint:(id)__unused object {
        @autoreleasepool {
            [[NSThread currentThread] setName:@"AFNetworking"];
            NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
            [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
            [runLoop run];
        }
    }
    
    + (NSThread *)networkRequestThread {
        static NSThread *_networkRequestThread = nil;
        static dispatch_once_t oncePredicate;
        dispatch_once(&oncePredicate, ^{
            _networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];
            [_networkRequestThread start];
        });
        return _networkRequestThread;
    }
    

    此处添加 port 只是为了让 RunLoop 不至于退出,并没有用于实际的发送消息
    当需要这个后台线程执行任务时,AFNetworking 通过调用 [NSObject performSelector:onThread:..] 将这个任务扔到了后台线程的 RunLoop 中实现消息发送

    - (void)start {
        [self.lock lock];
        if ([self isCancelled]) {
            [self performSelector:@selector(cancelConnection) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
        } else if ([self isReady]) {
            self.state = AFOperationExecutingState;
            [self performSelector:@selector(operationDidStart) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
        }
        [self.lock unlock];
    }
    

    timer

    三种创建方式

    1、第一种创建方式timer(系统会默认添加到Runloop的NSDefaultRunLoopMode中)

    1),scheduledTimerWithTimeInterval: invocation: repeats:
    2),scheduledTimerWithTimeInterval: target: selector: userInfo: repeats:
    
    //初始化NSTimer---1主线程直接
     [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timeup) userInfo:nil repeats:YES];
    - (void)timeup {
       NSLog(@"时间到%@",[NSThread currentThread]);
    }
    

    运行结果主线程直接启动不需要代码添加到runloop

    2020-05-09 16:50:27.279293+0800 demoTest[85568:2475995] 时间到<NSThread: 0x600002a06880>{number = 1, name = main}
    2020-05-09 16:50:28.278690+0800 demoTest[85568:2475995] 时间到<NSThread: 0x600002a06880>{number = 1, name = main}
    2020-05-09 16:50:29.279113+0800 demoTest[85568:2475995] 时间到<NSThread: 0x600002a06880>{number = 1, name = main}
    2020-05-09 16:50:30.278079+0800 demoTest[85568:2475995] 时间到<NSThread: 0x600002a06880>{number = 1, name = main}
    2020-05-09 16:50:31.279187+0800 demoTest[85568:2475995] 时间到<NSThread: 0x600002a06880>{number = 1, name = main}
    

    2、第二种创建方式timer(代码添加到runloop)

    1),timerWithTimeInterval: target: selector: userInfo: repeats:
    2),timerWithTimeInterval: invocation: repeats:
    
       //初始化NSTimer---2
      NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timeup) userInfo:nil repeats:YES];
      [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
    
    - (void)timeup {
        NSLog(@"NSTimer---2 %@",[NSThread currentThread]);
    }
    

    运行结果

    2020-05-09 16:55:57.607139+0800 demoTest[85709:2481598] NSTimer---2 <NSThread: 0x6000036e2900>{number = 1, name = main}
    2020-05-09 16:55:58.606587+0800 demoTest[85709:2481598] NSTimer---2 <NSThread: 0x6000036e2900>{number = 1, name = main}
    2020-05-09 16:55:59.606793+0800 demoTest[85709:2481598] NSTimer---2 <NSThread: 0x6000036e2900>{number = 1, name = main}
    2020-05-09 16:56:00.606252+0800 demoTest[85709:2481598] NSTimer---2 <NSThread: 0x6000036e2900>{number = 1, name = main}
    2020-05-09 16:56:01.606854+0800 demoTest[85709:2481598] NSTimer---2 <NSThread: 0x6000036e2900>{number = 1, name = main}
    

    3、第三种创建方式timer(代码添加到runloop)

    initWithFireDate: interval: target: selector: userInfo: repeats:
    
     //初始化NSTimer---3
     NSTimer *timer= [[NSTimer alloc]initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:5]interval:1 target:self selector:@selector(timeup) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
    - (void)timeup {
        NSLog(@"NSTimer---3 %@",[NSThread currentThread]);
    }
    

    运行结果

    2020-05-09 16:58:47.493477+0800 demoTest[85782:2484896] NSTimer---3 <NSThread: 0x6000012bc240>{number = 1, name = main}
    2020-05-09 16:58:48.493312+0800 demoTest[85782:2484896] NSTimer---3 <NSThread: 0x6000012bc240>{number = 1, name = main}
    2020-05-09 16:58:49.493144+0800 demoTest[85782:2484896] NSTimer---3 <NSThread: 0x6000012bc240>{number = 1, name = main}
    

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

    NSLog(@"当前线程:%@",[NSThread currentThread]);
        //创建一个runloop监听者
        CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(),kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
    
            NSLog(@"监听runloop状态改变---%zd",activity);
        });
    
        //为runloop添加一个监听者
        CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
        CFRelease(observer);
    

    运行结果:能runloop状态

    2020-05-10 17:33:08.727344+0800 demoOne[35093:1679140] 当前线程:<NSThread: 0x600002ae8240>{number = 1, name = main}
    2020-05-10 17:33:39.321042+0800 demoOne[35093:1679140] 监听runloop状态改变---2
    2020-05-10 17:33:39.321231+0800 demoOne[35093:1679140] 监听runloop状态改变---4
    2020-05-10 17:33:39.322211+0800 demoOne[35093:1679140] 监听runloop状态改变---2
    2020-05-10 17:33:39.322354+0800 demoOne[35093:1679140] 监听runloop状态改变---4
    2020-05-10 17:33:39.324036+0800 demoOne[35093:1679140] 监听runloop状态改变---2
    2020-05-10 17:33:39.324189+0800 demoOne[35093:1679140] 监听runloop状态改变---4
    2020-05-10 17:33:39.324511+0800 demoOne[35093:1679140] 监听runloop状态改变---2
    2020-05-10 17:33:39.324640+0800 demoOne[35093:1679140] 监听runloop状态改变---4
    2020-05-10 17:33:39.337857+0800 demoOne[35093:1679140] 监听runloop状态改变---2
    2020-05-10 17:33:39.338037+0800 demoOne[35093:1679140] 监听runloop状态改变---4
    2020-05-10 17:33:39.338532+0800 demoOne[35093:1679140] 监听runloop状态改变---2
    

    具体常用事例:tableview中渲染多张图片出现卡顿问题,可用runloop循环使用观察者每次只渲染几张大图解决卡顿

    OB实例(加载1024x1024大图)
    typedef void(^runloopBlock) (void);
    
    @interface ViewController ()<UITableViewDelegate,UITableViewDataSource>
    @property (nonatomic,strong) UITableView * tableView;
    @property (nonatomic,strong) NSMutableArray * taskes;
    @property (nonatomic,assign) NSInteger maxQueueLength;
    @end
    
    static NSString * ID = @"IDCELL";
    @implementation ViewController
    - (void)viewDidLoad {
        [super viewDidLoad];
       
        
        //让runloop不休眠(一直存活,一直走"唤醒")runloop添加了timer 瞬间观察到runloop
        [NSTimer scheduledTimerWithTimeInterval:0.001 target:self selector:@selector(timeMethond) userInfo:nil repeats:YES];
        _taskes = [[NSMutableArray alloc]init];
        _maxQueueLength = 9 ;
        //添加观察者
        [self addRunloopObserver];
        
         [self.view addSubview:self.tableView];
    }
    
    - (void)timeMethond{
        //不做任何事只为runloop唤醒
    }
    
    
    - (UITableView *)tableView{
        _tableView=[[UITableView alloc]initWithFrame:self.view.bounds style:UITableViewStyleGrouped];
        [_tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:ID];
        _tableView.delegate=self;
        _tableView.dataSource=self;
        return _tableView;
    }
    
    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
        return 140;
    }
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
        
        return 100;
    }
    
    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
        
        return 1;
    }
    
    -(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
        UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:ID];
        cell.selectionStyle = UITableViewCellSelectionStyleNone;
        for (NSInteger i = 1; i <= 4; i++) {
            [[cell.contentView viewWithTag:i] removeFromSuperview];
            //优化卡顿
            [self addTaskes:^{
                  [ViewController addImageWith:cell index:i];
            }];
    //        //卡顿
    //        [ViewController addImageWith:cell index:i];
        }
    
        return cell;
    }
    
    
    #pragma mark 关于Runloop C语言
    - (void)addRunloopObserver{
        //获取runloop
        CFRunLoopRef runloop= CFRunLoopGetCurrent();
        //初始化runloop观察者上下文结构体
        CFRunLoopObserverContext context = {0,(__bridge void *)(self),&CFRetain,&CFRelease,NULL};
        //定义观察者
        static CFRunLoopObserverRef defaultObserver;
        defaultObserver = CFRunLoopObserverCreate(NULL, kCFRunLoopBeforeWaiting, YES, NSIntegerMax-999, &callBack, &context);
        //观察添加到当前runloop
        CFRunLoopAddObserver(runloop, defaultObserver, kCFRunLoopCommonModes);
        CFRelease(defaultObserver);
    }
    
    //runloop中事件的执行体
    static void callBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity,void * info){
        NSLog(@"唤醒");
        ViewController * VC = (__bridge ViewController *)info;
        if (VC.taskes.count==0) {
            return;
        }
        //取出任务
        runloopBlock block = VC.taskes.firstObject;
        block();
        [VC.taskes removeObjectAtIndex:0];
    }
    
    //添加block 任务
    - (void)addTaskes:(runloopBlock)task{
        [self.taskes addObject:task];
        if (self.taskes.count > _maxQueueLength) {
            [self.taskes removeObjectAtIndex:0];
        }
    }
    
    + (void)addImageWith:(UITableViewCell *)cell index:(NSInteger)index{
        UIImageView * imageV = [[UIImageView alloc]initWithFrame:CGRectMake(20*index+80*(index-1),5 , 80, 90)];
        imageV.tag = index;
        NSString * path = [[NSBundle mainBundle] pathForResource:@"imagetest" ofType:@"png"];
        UIImage * image = [UIImage imageWithContentsOfFile:path];
        imageV.image =  image;
        [UIView transitionWithView:cell.contentView duration:0.3 options:(UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionTransitionCrossDissolve) animations:^{
            [cell.contentView addSubview:imageV];
        } completion:nil];
    }
    
    1024X1024大图

    七、补充

    定时器:NSTimer 与GCD

    NSTimer创建
    @property(nonatomic,strong)NSTimer* timer;
     self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timeup) userInfo:nil repeats:YES];
        - (void)timeup {
           NSLog(@"时间到%@",[NSThread currentThread]);
        }
    

    运行结果

    2020-05-10 16:46:28.031652+0800 demoOne[34828:1650155] 时间到<NSThread: 0x600002fec380>{number = 1, name = main}
    2020-05-10 16:46:29.032614+0800 demoOne[34828:1650155] 时间到<NSThread: 0x600002fec380>{number = 1, name = main}
    

    取消timer

     [self.timer invalidate];
     self.timer = nil;
    
    GCD timer创建
    @property(nonatomic,strong)dispatch_source_t gcdTimer;
    //0.创建一个队列
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
        //1.创建一个GCD的定时器
        /*
         第一个参数:说明这是一个定时器
         第四个参数:GCD的回调任务添加到那个队列中执行,如果是主队列则在主线程执行
         */
        dispatch_source_t gcdTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    
        //2.设置定时器的开始时间,间隔时间以及精准度
        //设置开始时间,三秒钟之后调用,注:GCD中的时间为纳秒NSEC_PER_SEC,3.0 *NSEC_PER_SEC即为3秒
        dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW,3.0 *NSEC_PER_SEC);
        //设置定时器工作的间隔时间
        uint64_t intevel = 1.0 * NSEC_PER_SEC;
    
        /*
         第一个参数:要给哪个定时器设置
         第二个参数:定时器的开始时间DISPATCH_TIME_NOW表示从当前开始
         第三个参数:定时器调用方法的间隔时间
         第四个参数:定时器的精准度,如果传0则表示采用最精准的方式计算,如果传大于0的数值,则表示该定时切换可以接收该值范围内的误差,通常传0
         该参数的意义:可以适当的提高程序的性能
         注意点:GCD定时器中的时间以纳秒为单位(面试)
         */
        dispatch_source_set_timer(gcdTimer, start, intevel, 0 * NSEC_PER_SEC);
        //3.设置定时器开启后回调的方法
        /*
         第一个参数:要给哪个定时器设置
         第二个参数:回调block
         */
        dispatch_source_set_event_handler(gcdTimer, ^{
            NSLog(@"------%@",[NSThread currentThread]);
        });
    
        //4.执行定时器
        dispatch_resume(gcdTimer);
     //注意:dispatch_source_t本质上是OC类,在这里是个局部变量,需要强引用
        self.gcdTimer = gcdTimer;
    

    运行结果:在子线程

    2020-05-10 16:49:08.220418+0800 demoOne[34883:1653302] ------<NSThread: 0x600003492ec0>{number = 7, name = (null)}
    2020-05-10 16:49:09.219542+0800 demoOne[34883:1653302] ------<NSThread: 0x600003492ec0>{number = 7, name = (null)}
    2020-05-10 16:49:10.219824+0800 demoOne[34883:1653302] ------<NSThread: 0x600003492ec0>{number = 7, name = (null)}
    2020-05-10 16:49:11.220617+0800 demoOne[34883:1653309] ------<NSThread: 0x6000034fd940>{number = 5, name = (null)}
    

    取消定时器

    // 演示如何取消定时器
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            // 取消定时器
            dispatch_cancel(self.gcdTimer);
        });
    

    异同点
    NSTimer:
    1、NSTimer是RunLoop的源
    2、自定义的需要加入mode
    3、 必须调用 invalidate 来停止其定时任务,并且NSTimer 对其Target是强引用,要注意Target 与 - NSTimer间造成的循环引用造成的内存泄漏
    4、不准时(原因:1.RunLoop循环处理的时间,可能某个时刻runloop需要处理很多任务;2.受RunLoop模式的影响,如果NSTimer没有加入到NSRunLoopCommonModes的话,就会受到UITrackingRunLoopMode和NSDefaultRunLoopMode的切换影响)

    GCD的timer
    1、gcd的timer是dispatch的源
    2、不需要加入mode,GCD内部已经将runloop进行封装
    3、精度很高

    持续更新~

    相关文章

      网友评论

          本文标题:iOS runloop 简述及实例

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