# RunLoop 的应用

> 了解了 RunLoop 的底层原理以及特点后我们有必要想一想它可以应用在什么地方？现在归纳下常见的应用场景

## NSTimer

我们常常写的 **NSTimer** 都是在的默认的运行状态下执行的（**NSDefaultRunLoopMode**）。所以我们会经常遇到 NSTimer 执行不准去的问题。因为 RunLoop 是有不同的运行状态的，当我们 UI 滚动的时候从 **NSDefaultRunLoopMode** 切换到 **UITrackingRunLoopMode**，所以添加到 **NSDefaultRunLoopMode** 状态下的事件是不会执行的，为了达到定时器准确的目的有2种方法。方法一：必须根据具体需求给 NSTimer 指定具体的  **CFRunLoopModeRef**。方法二：利用 GCD 的 timer 不会受 **NSDefaultRunLoopMode** 影响的特点。

```objective-c
//默认状态下的 NSTimer
[NSTimer scheduledTimerWithTimeInterval:2 repeats:YES block:^(NSTimer * _Nonnull timer) {
    NSLog(@"我在执行了");
}];
```

```objective-c
//方法1:给 NSTimer 指定运行状态
NSTimer *timer = [NSTimer timerWithTimeInterval:2 target:self selector:@selector(show) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
```

```objective-c
 //方法2:GCD 的单位是纳秒。使用 GCD 创建的 timer 正常创建后不会执行，因为创建后设置了指定的时间后触发，所以当代码运行到最后一行的时候，Timer 还没执行，就被销毁了。所以我们必须设置一个属性去保存它。

    //1、创建队列
    dispatch_queue_t queue = dispatch_get_main_queue();
    //2、创建 timer
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    //    self.timer = timer;
    //3、设置 timer 的参数：精准度、时间间隔
    //第三个参数为 GCD timer 的精准度
    dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
    //4、为 Timer 设置任务
    dispatch_source_set_event_handler(timer, ^{
        NSLog(@"%@",[NSRunLoop currentRunLoop]);
    });
    //5、执行任务
    dispatch_resume(timer);
```

## ImageView显示\(PerformSelector\)

UITableView 在滚动的时候一个优化点之一就是 UIImageView 的显示，通常需要根据网络去下载图片。所以如果用户快速滚动列表的时候，如果立马下载并显示图片的话，势必会对 UI 的刷新产生影响，直观的表现就是会卡顿，**FPS** 达不到60。

利用 RunLoop 可以实现这个效果，就是给下载并显示图片的方法指定 **NSRunLoopMode**。

```objective-c
- (IBAction)clickLoadIMage:(id)sender {
    //[self.imageview performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@"test"] afterDelay:2];
    [self performSelector:@selector(downloadAndShowImage) withObject:nil afterDelay:2 inModes:@[NSDefaultRunLoopMode,UITrackingRunLoopMode]];
}

- (void)downloadAndShowImage{
    self.imageview.image = [UIImage imageNamed:@"test"];
}
```

## 常驻线程

我们需要常驻线程，那么就需要线程不要销毁，那么一些做法比如设置任务死循环，那么线程就不会销毁；将当前线程强引用不要销毁等都存在问题。最好的方法是为当前线程设置合理的 RunLoop

```objective-c
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    LBPThread *thred = [[LBPThread alloc] initWithTarget:self         selector:@selector(showThreadLife) object:nil];
    self.lbpThread = thred;
    [thred start];
}

- (void)showThreadLife{
    NSLog(@"---show----");
}

- (IBAction)clickLoadIMage:(id)sender {
    NSLog(@"%s",__func__);
    [self performSelector:@selector(contactWithTwoThread) onThread:self.lbpThread withObject:nil waitUntilDone:YES];
}

- (void)contactWithTwoThread{
    NSLog(@"----contactWithTwoThread----");
}
```

![](https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/WX20180806-101917@2x.png)

我们都知道 RunLoop 存在必须要有一个 Timer 或者 Source。

```objective-c
//方法1:添加 Port 的 Source。Sourcr1
- (void)showThreadLife{
    NSLog(@"---show----");
    //子线程的 RunLoop 是需要自己手动创建并添加；RunLoop 如果不要销毁那么至少存在一个 Timer 或 Source
    [[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
    [[NSRunLoop currentRunLoop] run];
}
//方法2
- (void)showThreadLife{
    NSLog(@"---show----");
    //子线程的 RunLoop 是需要自己手动创建并添加；RunLoop 如果不要销毁那么至少存在一个 Timer 或 Source

    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:3 target:self selector:@selector(test) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
    [[NSRunLoop currentRunLoop] run];
}
```

注意：添加 Observer 是没有效果的。

## 自动释放池

自动释放池什么时候创建和释放

创建时间：第一次进入 RunLoop 的时候

释放时间：RunLoop 退出的时候

其他情况：当 RunLoop 将要休眠的时候释放，然后创建一个新的

**\_wrapRunLoopWithAutoreleasePoolHandler**  **0x1**

**\_wrapRunLoopWithAutoreleasePoolHandler**  **0xa0**

0x1 和 0xa0 是十六进制的数，对应十进制为1和160。





参考文章：

http://www.cocoachina.com/ios/20180515/23380.html

http://www.cocoachina.com/ios/20170417/19075.html

https://www.jianshu.com/p/4c38d16a29f1

https://www.cnblogs.com/kenshincui/p/6823841.html

https://juejin.im/entry/599c13bc6fb9a0248926a77d

https://blog.ibireme.com/2015/05/18/runloop/



