Runloop

本文深入剖析了RunLoop的工作原理及其核心功能,包括保持程序运行、监听多种事件类型等。详细介绍了RunLoop的内部结构,如CFRunLoopRef等五个关键类,并探讨了不同模式的作用与应用场景。此外,还提供了定时器、子线程Runloop及线程间通讯的实现示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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:系统的内核事件,具有唤醒线程的能力,捕捉到事件后交给source0来处理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值