Runloop

作者: yahibo | 来源:发表于2019-05-19 12:45 被阅读0次

    Runloop本质就是一个Event Loopdo while循环,接收->等待->处理
    目的:
    1、保持程序不退出;
    2、负责监听事件,时钟,网络,触摸等。

    RunLoop是一个对外的接口在CoreFoundation里边关于RunLoop5个类:

    CFRunLoopRef
    CFRunLoopModeRef
    CFRunLoopSourceRef
    CFRunLoopTimerRef
    CFRunLoopOberverRef
    

    一个RunLoop有多个mode,一个mode包含Source、Observer、Timer。每次调用RunLoop的主函数时,只能指定其中一个Mode,这个Mode被称作CurrentMode。如果需要切换Mode,只能退出Loop,再重新指定一个Mode进入。这样做主要为了分隔开不同组的Source、Observer、Timer使其互不影响。

    runloop.png

    Mode官方提供有5种,一般常用的Mode有三种:

    NSDefaultRunLoopMode 默认模式,在RunLoop没有指定Mode的时候,默认就跑在DefaultMode下。一般情况下APP都运行在这个mode下;
    UITrackingRunLoopMode 一般作用于ScrollView滚动的时候的模式,保证滑动的时候不受其他事件影响;
    NSRunLoopCommonModes 是一种组合模式包含了以上两种模式。
    
    1、定时加入runloop中
    @interface ViewController ()
    @end
    @implementation ViewController
    - (void)viewDidLoad {
        [super viewDidLoad];
        //1、创建Timer 测试3种model
        NSTimer *timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(timerMethod) userInfo:nil repeats:YES];
        //将timer加入到Runloop中
        [[NSRunLoop currentRunLoop]addTimer:timer forMode:NSDefaultRunLoopMode];
    }
    //测试1
    -(void)timerMethod{
        static int a = 0;
        NSLog(@"测试1:线程%@ - %d",[[NSThread currentThread] name],a++);
    }
    @end
    

    创建定时器后需要加入runloop中由runloop循环检测定时器的执行情况,否则定时器不会执行。如果timer调用fire,定时器只执行一次。

    2、子线程中加入timer。一条线程能够存活的原因是什么?有任务。
    @interface ViewController ()
    @property(nonatomic,assign) BOOL finished;
    @end
    @implementation ViewController
    - (void)viewDidLoad {
        [super viewDidLoad];
        _finished = YES;
        NSThread *thread = [[NSThread alloc] initWithBlock:^{
            NSTimer *timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(timerThread) userInfo:nil repeats:YES];
            [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
            //创建一个runloop不断执行、使用while+flag控制结束
            while (self->_finished) {
                [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]];
            }
            NSLog(@"结束");
        }];
        [thread start];
    }
    //测试2 在线程中的定时器必须保持线程不被释放
    -(void)timerThread{
        static int a = 0;
        NSLog(@"测试2:线程%@ - %d",[[NSThread currentThread] name],a++);
    }
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        _finished = NO;
    }
    @end
    

    若在子线程中使用runloop需要保持线程是存活状态,保持线程中一直有任务执行,否则线程执行完成后创建的runloop将被终止释放。因此需要在子线程中调用run方法保持runloop一直处于工作状态,如需终止runloop,设置标志位,在外部触发终止。

    3、线程间的通讯
    @interface ViewController ()
    @property(nonatomic,assign) BOOL finished;
    @end
    @implementation ViewController
    - (void)viewDidLoad {
        [super viewDidLoad];
        _finished = YES;
        NSThread *thread = [[NSThread alloc] initWithBlock:^{
            NSLog(@"开始");
            //创建一个runloop不断执行、run很难结束任务,使用while+flag控制结束
            while (self->_finished) {
                [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]];
            }
        }];
        [thread start];
        [self performSelector:@selector(threadContact) onThread:thread withObject:self waitUntilDone:NO];
    }
    //测试3
    -(void)threadContact{
           NSLog(@"测试3:线程通讯 线程%@",[NSThread currentThread]);
    }
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        _finished = NO;
    }
    @end
    
    4、GCD定时器对应runloop中的source,封装了对runloop的使用
    @interface ViewController ()
    @property (nonatomic,strong)dispatch_source_t timer;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        //GCD 定时器
        _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));
        //设置定时器
        dispatch_source_set_timer(_timer, DISPATCH_TIME_NOW, 1.0*NSEC_PER_SEC, 0);
        //设置回调
        dispatch_source_set_event_handler(_timer, ^{
            NSLog(@"%@",[NSThread currentThread]);
        });
        dispatch_resume(_timer);//启动定时器
    }
    @end
    

    Source:事件源(输入源)
    按照函数调用栈Source分为两种:
    Source0:非内核事件
    Source1:系统的内核事件

    相关文章

      网友评论

          本文标题:Runloop

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