runloop的应用

308 阅读4分钟

GCD定时器

/** 定时器(这里不用带*,因为dispatch_source_t就是个类,内部已经包含了*) */
@property (nonatomic, strong) dispatch_source_t timer;


int count = 0;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    // 获得队列
//    dispatch_queue_t queue = dispatch_get_global_queue(0, 0); //全局并发队列
    dispatch_queue_t queue = dispatch_get_main_queue(); //主队列
    
    // 创建一个定时器(dispatch_source_t本质还是个OC对象)
    self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    
    // 设置定时器的各种属性(几时开始任务,每隔多长时间执行一次)
    // GCD的时间参数,一般是纳秒(1秒 == 10的9次方纳秒)
    // 何时开始执行第一个任务
    // dispatch_time(DISPATCH_TIME_NOW, 3.0 * NSEC_PER_SEC) 比当前时间晚3秒
    dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC));
    uint64_t interval = (uint64_t)(1.0 * NSEC_PER_SEC); //NSEC_PER_SEC 纳秒
    dispatch_source_set_timer(self.timer, start, interval, 0);
    
    // 设置回调
    dispatch_source_set_event_handler(self.timer, ^{
        NSLog(@"------------%@", [NSThread currentThread]);
        count++;
        
//        if (count == 4) {
//            // 取消定时器
//            dispatch_cancel(self.timer);
//            self.timer = nil;
//        }
    });
    
    // 启动定时器
    dispatch_resume(self.timer);
}

NSTimer

- (void)timer
{
    NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
    // 定时器只运行在NSDefaultRunLoopMode下,一旦RunLoop进入其他模式,这个定时器就不会工作
    //    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
    
    // 定时器只运行在UITrackingRunLoopMode下,一旦RunLoop进入其他模式,这个定时器就不会工作
    //    [[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
    
    // 定时器会跑在标记为common modes的模式下
    // 标记为common modes的模式:UITrackingRunLoopMode和NSDefaultRunLoopMode
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}
- (void)timer2
{
    // 调用了scheduledTimer返回的定时器,已经自动被添加到当前runLoop中,而且是NSDefaultRunLoopMode
    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
    
    // 修改模式
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
    
    //如果不是主线程
    //所以只需要让子线程 Runloop run  就好了
    [[NSRunLoop currentRunLoop] run];
}
  • 02 第二部分 05Quartz2D 08 雪花定时器 动画计时器
    CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(timeChange)];
    
    //添加到主运行循环
    [link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];

ImageView显示

- (void)useImageView
{
    // 只在NSDefaultRunLoopMode模式下显示图片 (不卡顿显示)
    [self.imageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@"placeholder"] afterDelay:3.0 inModes:@[NSDefaultRunLoopMode]];
}

PerformSelector

self performSelector:<#(nonnull SEL)#> withObject:<#(nullable id)#> afterDelay:<#(NSTimeInterval)#> inModes:<#(nonnull NSArray<NSRunLoopMode> *)#>

    // 只在NSDefaultRunLoopMode模式下显示图片
    [self.imageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@"placeholder"] afterDelay:3.0 inModes:@[NSDefaultRunLoopMode]];

常驻线程

  • 常驻线程:线程不死:经常要在后台(子线程)做耗时操作 如:==在后台监控用户的操作==
#import <Foundation/Foundation.h>

@interface XMGThread : NSThread

@end

#import "XMGThread.h"

@implementation XMGThread
- (void)dealloc
{
    //为了查看自定义线程是否死亡
    NSLog(@"%@ --- dealloc", self);
}
@end

/** 线程对象 */
@property (nonatomic, strong) XMGThread *thread;


//1.创建子线程 
    self.thread = [[XMGThread alloc] initWithTarget:self selector:@selector(run) object:nil];
    self.thread.name = @"开启的常驻线程";
    [self.thread start];
    
//想让线程不死要在线程中开启runLoop  开启不死线程
- (void)run
{
    NSLog(@"----------run----%@", [NSThread currentThread]);

    //1.方法1 addPort 
    [[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
    
    //2.方法2 addTimer 
    // 新建NSTimer对象
    NSTimer *timer = [NSTimer timerWithTimeInterval:5.0 target:self selector:@selector(show) userInfo:nil repeats:YES];
    // 将NSTimer添加到RunLoop中,并且告诉系统,当前Tiemr只有在RunLoop的默认模式下才有效
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
    
    
    //如果runLoop 中没有添加 Source、Observer、Timer  runLoop就会直接退出 
    [[NSRunLoop currentRunLoop] run];  //启动runLoop



    NSLog(@"---------");
    //相当于
//    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
//    [[NSRunLoop currentRunLoop] runUntilDate:[NSDate distantFuture]];

}


/**
 * 这种方式虽然能保住线程的名,但是这条线程就无法处理其他行为(事件)
 */
//- (void)run
//{
//    NSLog(@"----------run----%@", [NSThread currentThread]);
//    
//    while (1); // 当前线程永远在处理这行代码
//    
//    NSLog(@"---------");
//}


//点击事件 调用方法
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:NO];

}

//唤醒子线程在子线程中做事情
- (void)test
{
    NSLog(@"----------test----%@", [NSThread currentThread]);
}

自动释放池

- (void)execute
{
    @autoreleasepool{
        [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(test) userInfo:nil repeats:YES];

        [[NSRunLoop currentRunLoop] run];
    }
}

RunLoop面试题

  • 什么是runLoop?
    • 从字面意思就是运行循环
    • 内部是一个do - while 循环,在循环内部不断处理各种事件(比如 source、timeer、observer)
    • 一个线程对应一个RunLoop,主线程runLoop 默认启动,子线程runLoop要手动启动
    • runLoop 只能选择一个模式启动,如果当前模式中没有source(source0、source1)、timer,那么就直接退出runLoop
  • 你在开发中怎么使用runLoop
    • 开启一个常驻线程,(让一个子线程不进入消亡状态,等待其他线程发来消息,处理其他事件)
      • 在子线程中开启一个定时器
      • 在子线程中进行长期监控一些行为
    • 可以控制定时器在特定模式下运行
    • 可以让某些事件(行为,任务)在某种特定的模式下运行
    • 可以添加Observer监听runLoop的状态,比如监听点击状态的处理(在点击之前处理一些事情)
  • 自动释放池在什么时候释放?
    • 在runLoop 睡眠之前释放 ==kCFRunLoopAfterWaiting==
    • 通过Observe监听RunLoop的状态,一旦监听到在RunLoop即将进入睡眠等待状态就释放自动释放池(kCFRunLoopBeforeWaiting)

自动释放池和RunLoop

  • kCFRunLoopEntry; //创建一个自动释放池
  • kCFRunLoopBeforeWaiting; //销毁自动释放池,创建一个新的自动释放池
  • kCFRunLoopExit; //销毁自动释放池