谈谈RunLoop

作者: kamto | 来源:发表于2017-07-31 17:15 被阅读4次

看了很多关于RunLoop的文章,还是对RunLoop的底层原理不能理解太多,可能是技术没达到水准的原因吧。如此,做个随笔记录下,等以后若是能懂一二的话再来修改。

一、 什么是RunLoop

官方对RunLoop的定义:RunLoop系统中和线程相关的基础架构的组成部分(和线程相关),一个RunLoop是一个事件处理环,系统利用这个事件处理环来安排事务,协调输入的各种事件。

曾网上看到过关于RunLoop的场景形容:我们把线程比作一辆跑车,把这辆跑车的主人比作RunLoop,那么在没有'主人'的时候,这个跑车的生命是直线型的,其启动,运行完之后就会废弃(没有人对其进行控制,'撞坏'被收回),当有了RunLoop这个主人之后,‘线程’这辆跑车的生命就有了保障,这个时候,跑车的生命是环形的,并且在主人有比赛任务的时候就会被RunLoop这个主人所唤醒,在没有任务的时候可以休眠,这样可以增加跑车的效率,也就是说RunLoop是为线程所服务的。

  • 一条线程对应一个RunLoop,主线程的RunLoop默认已经创建好了, 而子线程需要我们自己手动创建。
  • 一个runLoop包含若干个Model,每个Mode又包含若干个Source/Timer/Observer,每次RunLoop启动时,只能指定其中一个Mode,这个Mode被称作 CurrentMode。
  • 如果需要切换Mode,只能退出Loop,再重新指定一个Mode进入。
  • 获取主线程对应的RunLoop对象 mainRunLoop/CFRunLoopGetMain
  • 获取当前线程对应的RunLoop对象 currentRunLoop/CFRunLoopGetCurrent
  • NSDefaultRunLoopMode:App的默认Mode,通常主线程是在这个Mode下运行
  • UITrackingRunLoopMode:界面跟踪Mode,用于ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
  • UIInitializationRunLoopMode: 在刚启动App时第进入的第一个 Mode,启动完成后就不再使用
  • GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到
  • NSRunLoopCommonModes: 这是一个伪模式,为一组runloop mode的集合,将timer加入此模式意味着在Common Modes中包含的所有模式下都可以处理。

二、 RunLoop的常见用例

  1. 在子线程中执行perform selector
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [self performSelector:@selector(method) onThread:[NSThread  currentThread]  withObject:nil waitUntilDone:NO];
         [[NSRunLoop currentRunLoop] run];
});

当perform selector在后台线程中执行的时候,这个线程必须有一个开启的runLoop,否则不会执行

  1. 线程执行完任务会死亡,runLoop为线程注入“活力”
- (void)test{
        NSThread *thread = [[NSThread alloc] initWithTarget:self  selector:@selector(threadRun) object:nil];
       [thread start];
       [self performSelector:@selector(method) onThread: thread withObject:nil waitUntilDone:NO];
}
- (void)threadRun{
     NSLog(@"---%s--",__func__);
}

performSelector 不会执行,尽管thread是存在的,但这个线程在执行threadRun任务之后已经死亡了,也就不会再执行后面的performSelector,正常情况下,后台线程执行完任务之后就处于死亡状态,避免这种情况的发生可以利用RunLoop,并且给它一个Source这样来保证线程依旧还在

- (void)threadRun{
NSLog(@"---%s--",__func__);
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[[NSPort alloc]init] forMode:NSDefaultRunLoopMode];
[runLoop run];
}

3.在子线程添加timer

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:0.5 repeats:TRUE block:^(NSTimer * _Nonnull timer) {
NSLog(@"action");
}];
[timer fire];
[[NSRunLoop currentRunLoop] run];
});
  1. 图片加载时机
// 只在NSDefaultRunLoopMode模式下显示图片(为了使滚动更加流畅,scroll滚动的时候不显示图片,尤其是当图片很大的时候尤其有意义)

[self.imageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@"appointment_duty_img"] afterDelay:1.0 inModes:@[NSDefaultRunLoopMode]];
  1. tableView或者collectionView里面每个cell需要显示几张图片,而且这些图片都是很大的图
    定义RunLoop监听,监听回调方法,每次执行一次绘制任务,执行完就把任务删除
typedef void(^RunloopBlock)(void);

@property (nonatomic, strong) NSMutableArray *tasks;

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
***code
[self trySetImage]
***code
}

- (void)trySetImage{
__weak typeof(self) weakSelf = self;
[self addTask:^{
   [weakSelf setImage1];
}];
[self addTask:^{
   [weakSelf setImage2];
}];

[self addTask:^{
    [weakSelf setImage3];
}];
}

- (void)addTask:(RunloopBlock)task{
     [_tasks addObject:task];
}

- (void)addRunloopObserver{
CFRunLoopObserverContext context = {
0,
(__bridge void *)self,
&CFRetain,
&CFRelease,
NULL
};
static CFRunLoopObserverRef observer;
observer = CFRunLoopObserverCreate(NULL, kCFRunLoopAllActivities, YES, 0, &ObserverCallback, &context);
CFRunLoopAddObserver( CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
CFRelease(observer);
}
static void ObserverCallback (CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){
/*
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(@"即将退出");
break;
default:
break;
}
*/
ViewController *vc  = (__bridge ViewController *)info;
if (!vc.tasks.count) return;
RunloopBlock task  = [vc.tasks firstObject];
task();
[vc.tasks removeObjectAtIndex:0];
}

特别说明:本随笔大部分借鉴了一些网上的文章,只作记录和学习,不作其他用途。
demo地址

相关文章

  • 谈谈RunLoop

    看了很多关于RunLoop的文章,还是对RunLoop的底层原理不能理解太多,可能是技术没达到水准的原因吧。如此,...

  • 简单谈谈RunLoop

    1、RunLoop定义 从字面上看,run是运行,执行的意思,loop是循环的意思,其实RunLoop就是运行循环...

  • 谈谈RunLoop底层

    RunLoop是什么? RunLoop是iOS/Mac OS开发中比较重要的知识点,它贯穿程序运行的整个过程。它是...

  • 谈谈 iOS RunLoop 底层

    RunLoop是什么? RunLoop是iOS/Mac OS开发中比较重要的知识点,它贯穿程序运行的整个过程。它是...

  • RunLoop

    RunLoop简单概述 RunLoop相关类 RunLoop逻辑处理 RunLoop实践 RunLoop简单概述 ...

  • 2019 iOS面试题-----RunLoop数据结构、RunL

    RunLoop概念 RunLoop的数据结构 RunLoop的Mode RunLoop的实现机制 RunLoop与...

  • 谈谈你对RunLoop的理解。

    一个程序从main函数开始,函数执行完毕之后就会退出,iOS程序也是一样的,但是我们从没看到过iOS程序打开之后直...

  • iOS RunLoop(1)RunLoop简介

    iOS RunLoop(1)RunLoop简介iOS RunLoop(2)RunLoop相关类iOS RunLo...

  • iOS RunLoop(2)RunLoop相关类

    iOS RunLoop(1)RunLoop简介iOS RunLoop(2)RunLoop相关类iOS RunLo...

  • iOS RunLoop(3)RunLoop原理

    iOS RunLoop(1)RunLoop简介iOS RunLoop(2)RunLoop相关类iOS RunLo...

网友评论

    本文标题:谈谈RunLoop

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