RunLoop的基本应用

1:利用RunLoop设置图片


2:利用RunLoop创建精准定时器

3:利用RunLoop开启一条常驻线程



具体代码如下




//
//  ViewController.m
//  RunLoop
//
//  Created by fe on 2016/11/2.
//  Copyright © 2016年 fe. All rights reserved.
//

#import "ViewController.h"

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imagView;
@property (weak, nonatomic) IBOutlet UITextView *textView;
@property (nonatomic , strong) dispatch_source_t timer;
//为防止创建的子线程在处理完一个事件之后被释放需要用强指针引用
@property (nonatomic , strong) NSThread *residentThred;
@end

@implementation ViewController

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    [self residentThread];
}


//-------------------------利用RunLoop设置图片------------------------
- (void)setImageWithRunLoop
{
    
    /*
     1:在RunLoop默认的运行模式下加载图片
     在这里有一个imageView设置延时两秒加载图片,同时有一个textView可以上下滑动查看内容
     在默认情况下加载图片操作是在NSDefaultRunLoopMode模式下完成
     在这种情况下,调用该方法后,滑动textView,就不能设置图片,等到滑动结束后再加载图片
     */
    [self.imagView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@"cj"] afterDelay:2.0 ];
    
    
    /*
     2:在RunLoop的NSDefaultRunLoopMode,UITrackingRunLoopMode运行模式下加载图片
     在这里有一个imageView设置延时两秒加载图片,同时有一个textView可以上下滑动查看内容
     在默认情况下加载图片操作是在NSDefaultRunLoopMode模式下完成
     在这种情况下,调用该方法后,滑动textView的同时就可以加载图片
     */
    [self.imagView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@"cj"] afterDelay:2.0 inModes:@[NSDefaultRunLoopMode,UITrackingRunLoopMode]];
}



//-------------------------利用RunLoop创建精准定时器------------------------
-(void)timer1
{
    /*
     第一种定时器用法,这种方式创建的定时器,
     系统默认添加到NSRunLoop中的NSDefaultRunLoopMode模式中,
     但是当用户和应用交互发生触摸滑动等事件时,
     RunLoop的模式会切换到UITrackingRunLoopMode,
     此时定时器就不再工作,因为定时器只在,默认被添加到的NSDefaultRunLoopMode
     模式下工作。
     */
    [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(go) userInfo:nil repeats:YES];
}
-(void)timer2
{
    /*
     第二种定时器用法,这种方式创建的定时器,如果添加到NSRunLoop,
     并设置RunLoop模式为NSDefaultRunLoopMode,
     当用户和应用交互发生触摸滑动等事件时,
     RunLoop的模式会切换到UITrackingRunLoopMode,
     此时定时器就不再工作,因为定时器只在NSDefaultRunLoopMode模式下工作。
     
     如果把用这种方式创建的定时器,添加到NSRunLoop,
     并设置RunLoop模式为UITrackingRunLoopMode,
     在这种情况下,只有当用户和应用交互发生触摸滑动等事件时,
     定时器才会工作。
     
     为了解决以上两种RunLoop运行模式造成的定时器,定时不准的问题。
     我们可以把定时器添加到RunLoop并设置运行模式为NSRunLoopCommonModes.
     在这种运行模式下,不管是默认状态,还是当用户和应用交互发生触摸滑动等事件时。
     定时器都可以正常工作。
     */
    //1:创建定时器
    NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(go) userInfo:nil repeats:YES];
    
    //2.1:把定时器添加到RunLoop中,定时器在NSDefaultRunLoopMode模式下工作。
    //[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
    
    //2.2:把定时器添加到RunLoop中,定时器在UITrackingRunLoopMode模式下工作。
    //[[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
    
    //2.3
    /*
     把定时器添加到RunLoop中,设置模式为NSRunLoopCommonModes,
     定时器在UITrackingRunLoopMode模式和NSDefaultRunLoopMode模式下都工作。
     
     NSRunLoopCommonModes是一种标记模式,被打上这种标记的模式有以下两种
     0 : contents = "UITrackingRunLoopMode"
     2 : contents = "kCFRunLoopDefaultMode"
     */
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
    NSLog(@"---------%@",[NSRunLoop currentRunLoop]);
    
    
}
-(void)GCDTimer
{
    /*
     使用GCD的定时器,不会受到RunloopMode的影响。
     */
    
    
    //0:创建队列
    dispatch_queue_t queue = dispatch_queue_create("cn.520.www", DISPATCH_QUEUE_CONCURRENT);
    
    //1:创建一个GCD定时器
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    self.timer = timer;
    
    //2:设置定时器的开始时间,间隔时间,精确度。精准度一般填0,表示没有误差。
    dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC, 00 * NSEC_PER_SEC);
    
    //3:定时器要调用的方法。
    dispatch_source_set_event_handler(timer, ^{
        NSLog(@"----------%@",[NSThread currentThread]);
    });
    
    //4:启动定时器。
    dispatch_resume(timer);
}
-(void)go
{
    NSLog(@"-----------");
}

//------------------------利用RunLoop开启一条常驻线程-----------------------
//常驻线程
- (void)residentThread
{
    /*
     如果创建的子线程没有手动开启RunLoop,并在RunLoop中添加Source和Timer
     则该线程在第一次处理完任务后,就不能再继续用来处理任务
     */
    //1:创建线程
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(firstTask) object:nil];
    //用强指针引用
    self.residentThred = thread;
    //2:开启线程
    [thread start];
}

- (void)firstTask
{
    NSLog(@"----%s----%@",__func__,[NSThread currentThread]);
    
    //我们开启的RunLoop不能为空,需要在运行模式中添加source或者timer(不能添加observer)
    //我们添加的source本身并没有什么实际意义,只是为了不让RunLoop休眠
    [[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
    
    //如果要保证我们创建的线程能在其它地方继续使用需要手动开启RunLoop
    [[NSRunLoop currentRunLoop] run];
}
- (void)secondTask
{
    NSLog(@"----%s----%@",__func__,[NSThread currentThread]);
}
- (IBAction)btnClick:(UIButton *)sender {
    //点击按钮调用我们事先创建好的子线程处理第二个任务
    [self performSelector:@selector(secondTask) onThread:self.residentThred withObject:nil waitUntilDone:YES ];
}
@end



ios常驻线程使用场景 ios实现常驻线程_运行模式