天生不是作曲家iOS iOS && Android

(iOS干货)关于RunLoop的详细整理

2016-07-18  本文已影响524人  奕十八

RunLoop

1.RunLoop的基本作用

2.RunLoop与线程

NSRunLoop *currentRunloop = [NSRunLoop currentRunLoop];

子线程创建的runloop还需要手动的开启:

[currentRunloop run];

2.RunLoop运行模式对NSTimer定时器的影响

    NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(task) userInfo:nil repeats:YES];

这里的task为一个普通的打印操作,这时候往控制器的view里拖一个Text View,

[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];

但是如果这样做的话,当我们停止滚动的时候定时器又不工作了

[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

GCD定时器

GCD定时器是不受运行模式的影响的,因此,以后尽量使用此定时器,,该定时器的具体参数如下所示:

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    //01 创建定时器对象
    /*
     第一个参数:是一个宏,表示要创建的是一个定时器
     第二个参数和第三个参数:默认总是传0 描述信息
     第四个参数:队列  决定代码块(dispatch_source_set_event_handler)在哪个线程中调用(主队列-主线程)
     */
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));

    //02 设置定时器
    /*
     第一个参数:定时器对象
     第二个参数:定时器开始计时的时间(开始时间)
     第三个参数:设置间隔时间 GCD的时间单位:纳秒
     第四个参数:精准度
     */
    //这句代码是设置开始两秒之后再调用
    dispatch_time_t t = dispatch_time(DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC);
    dispatch_source_set_timer(timer, t, 2.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);

    //03 GCD定时器时间到了之后要执行的任务
    dispatch_source_set_event_handler(timer, ^{
        NSLog(@"GCD----%@",[NSThread currentThread]);
    });

    //04 默认是在挂起状态,需要手动恢复执行
    dispatch_resume(timer);

    //如果没有一个强指针指向,一创建就回被释放。
    self.timer = timer;

}

CFRunLoopModeRef

CFRunloopSourceRef

分类(根据函数调用栈来区分):

CFRunLoopObserverRef

作用:监听运行循环的状态

selecter事件与RunLoop之间的关系

    [NSThread detachNewThreadSelector:@selector(task) toTarget:self withObject:nil];

    -(void)task{
    [self.imageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@"Snip20160713_9"] afterDelay:3.0];
    NSLog(@"+++++");
}

一个显而易见的问题就是我们在子线程中来设置显示图片,然而抛开这个问题不管,图片依旧不会被设置上去,因为在task中缺少一个运行循环,我们需要手动开启一个子运行循环才可以。

继续查看一下代码,图片会被设置到imageView上面吗?

    [NSThread detachNewThreadSelector:@selector(task) toTarget:self withObject:nil];

    -(void)task{
    NSRunLoop *runloop = [NSRunLoop currentRunLoop];
    [runloop run];

    [self.imageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@"Snip20160713_9"] afterDelay:3.0];
    NSLog(@"+++++");
}

答案是否定的,因为我们虽然开启了子运行循环,但是当我们开启这个循环的时候,当前循环里既没有source事件(包括timer事件),也没有selecter事件,于是循环立刻就退出了。正确的书写方式如下:

    [NSThread detachNewThreadSelector:@selector(task) toTarget:self withObject:nil];

    -(void)task{
    [self.imageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@"Snip20160713_9"] afterDelay:3.0];
      NSRunLoop *runloop = [NSRunLoop currentRunLoop];
    [runloop run];
    NSLog(@"+++++");
}

注意:runLoop是一个死循环,因此++++是不会打印的

使用RunLoop实现常驻线程

众所周知,我们手动开启的子线程在执行完任务之后就会销毁,而有时候我们需要一个子线程在执行完当前任务后,不要销毁,等我们需要的时候再来执行其它任务,这就用到了常驻线程。

- (IBAction)btn1:(id)sender {
    //01 创建线程,执行任务
    NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(run1) object:nil];

    //02 执行任务
    [thread start];

    self.thread = thread;
}

为了不让刚开启的线程销毁,我们需要给它添加一个运行循环,保证它不释放:

-(void)run1
{
    NSLog(@"run1---%@",[NSThread currentThread]);
    //001 获得当前线程对应的runloop对象
    NSRunLoop *currentRunloop = [NSRunLoop currentRunLoop];

    //002 为runloop添加input soucre或者是timer souce或selecter事件(最好就是一个基于端口的事件,这样就不会去执行不必要的方法)
    [currentRunloop addPort:[NSPort port] forMode:NSDefaultRunLoopMode];

//以下为其它保证程序运行的方案,不推荐使用。
    //[self performSelector:@selector(run3) withObject:nil afterDelay:3.0];
    //[NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(timerTest) userInfo:nil repeats:YES];

    //003 启动runloop
    //runUntilDate |run 内部都指定了运行模式为默认
    [currentRunloop run];

}

按钮2:

- (IBAction)goOnBtnClick:(id)sender {
    //让之前创建的线程继续执行
    [self performSelector:@selector(run2) onThread:self.thread withObject:nil waitUntilDone:YES];
}

-(void)run2
{
    NSLog(@"run2---%@",[NSThread currentThread]);
}

RunLoop的自动释放池

runloop的自动释放池什么时候创建释放?

720984-20151203120852377-618169617.png
上一篇 下一篇

猜你喜欢

热点阅读