(iOS开发) RunLoop与NSTimer的相关

本文详细探讨了RunLoop机制与NSTimer的关系,包括不同RunLoop模式下NSTimer的行为特性、如何确保NSTimer在各种情况下都能正常触发,以及如何使用GCD Timer替代NSTimer。

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

 在开发中经常会遇到这种情况,自己开启了个定时器,但是在某些情况下的时候,timer的方法不响应,但是timer仍然开启着;这种情况就是timer所在的Runloop暂时关闭了。


一、RunLoop 与NSRunLoopCommonModes

nstimer 使用scheduledTimerWithTimeInterval方法的时候,此时的nstimer会被加入到当前的线程的RunLoop中去,此时的线程是NSDefaultRonLoopMode

但是如果线程是主线程的时候,某些UI事件,比如UIScrollView的拖拽操作等,会将RunLoop切换成UITrackingRunLoopMode。而这时候加入到NSDefaultRonLoopMode中的nstimer是不会被执行的。

解决方式:将nstimer手动写入RunLoop,并指定为NSRunLoopCommonModes。或者在UIScrollView中,可以指定为UITrackingRunLoopMode。

代码:

    timer = [NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop]addTimer:timer forMode:NSRunLoopCommonModes];
//    [[NSRunLoop currentRunLoop]addTimer:timer forMode:UITrackingRunLoopMode];


二、timer与NSThread

    NSThread * thread1=[[NSThread alloc]initWithTarget:self selector:@selector(threadAction) object:nil];
    [thread1 start];

-(void)threadAction
{
    @autoreleasepool {
        [NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
        [[NSRunLoop currentRunLoop]run];
    }
}

-(void)timerAction
{
    NSLog(@"--------------");
}


三、timer 与GCD

GCD中的Timer应该是最灵活的,而且是多线程的。GCD中的Timer是靠Dispatch Source来实现的。

因此先需要声明一个dispatch_source_t本地变量:

<span style="font-size:14px;">@interface ViewController ()
{
    dispatch_source_t _timer;
}</span>


接着通过dispatch_source_create函数来创建一个专门的Dispatch Source,接着通过dispatch_source_set_timer函数来设置Timer的参数,注意这里的时间参数有些蛋疼。

开始时间的类型是dispatch_time_t,最好用dispatch_time或者dispatch_walltime函数来创建dispatch_time_t对象。如果需要Timer立即执行,可以传入dispatch_time(DISPATCH_TIME_NOW, 0)

internalleeway参数分别表示Timer的间隔时间和精度。类型都是uint64_t。间隔时间的单位竟然是纳秒。可以借助预定义的NSEC_PER_SEC宏,比如如果间隔时间是两秒的话,那interval参数就是:2 * NSEC_PER_SEC

leeway就是精度参数,代表系统可以延时的时间间隔,最高精度当然就传0。

然后通过dispatch_source_set_event_handler函数来设置Dispatch Source的事件回调,这里当然是使用Block了。

最后所有dispatch_source_t创建后默认都是暂停状态的,所以必须通过dispatch_resume函数来开始事件监听。这里就代表着开始Timer。

NSLog(@"主线程 %@", [NSThread currentThread]);
//间隔还是2秒
uint64_t interval = 2 * NSEC_PER_SEC;
//创建一个专门执行timer回调的GCD队列
dispatch_queue_t queue = dispatch_queue_create("my queue", 0);
//创建Timer
_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
//使用dispatch_source_set_timer函数设置timer参数
dispatch_source_set_timer(_timer, dispatch_time(DISPATCH_TIME_NOW, 0), interval, 0);
//设置回调
dispatch_source_set_event_handler(_timer, ^()
{
    NSLog(@"Timer %@", [NSThread currentThread]);
});
//dispatch_source默认是Suspended状态,通过dispatch_resume函数开始它
dispatch_resume(_timer);




当然现在Xcode中已经实现了直接跳出一个编辑好的方法块,只要先申明一个专门执行timer回调的GCD队列:代码如下

 //创建一个专门执行_mytimer回调的GCD队列
    dispatch_queue_t queue = dispatch_queue_create("myqueue", 0);
    
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
    dispatch_source_set_event_handler(timer, ^{
        //回调的方法
        [self timerAction];
    });
    dispatch_resume(timer);





文章转载自:https://www.mgenware.com/blog/?p=459

还有个关于RunLoop写的比较好的文章:http://www.jianshu.com/p/e4fc6aceec49









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值