在iOS开发中,计时器是一个非常重要的工具,用于在特定的时间间隔内执行某些任务。Objective-C提供了NSTimer
类来实现计时器功能。本文将详细介绍NSTimer
的使用方法、注意事项以及常见问题的解决方案。
NSTimer
是Foundation框架中的一个类,用于在指定的时间间隔后执行某个方法或代码块。它可以用于实现定时任务、轮询、动画等场景。
NSTimer
的工作原理是基于RunLoop的,它依赖于当前线程的RunLoop来触发计时器事件。因此,NSTimer
的精度和线程安全性是需要特别注意的问题。
NSTimer
提供了多种创建方法,开发者可以根据需求选择合适的方式来创建计时器。
+scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:
方法这是最常用的创建NSTimer
的方法之一。它会自动将计时器添加到当前线程的RunLoop中,并立即启动。
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:@selector(timerFired:)
userInfo:nil
repeats:YES];
timeInterval
: 计时器的时间间隔,单位为秒。target
: 计时器触发时调用的方法的对象。selector
: 计时器触发时调用的方法。userInfo
: 传递给计时器的用户信息,通常为nil
。repeats
: 是否重复执行,YES
表示重复,NO
表示只执行一次。+timerWithTimeInterval:target:selector:userInfo:repeats:
方法这个方法与scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:
类似,但它不会自动将计时器添加到RunLoop中。开发者需要手动将计时器添加到RunLoop中。
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0
target:self
selector:@selector(timerFired:)
userInfo:nil
repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
+scheduledTimerWithTimeInterval:repeats:block:
方法从iOS 10开始,NSTimer
提供了基于Block的创建方法,使得代码更加简洁。
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0
repeats:YES
block:^(NSTimer * _Nonnull timer) {
[self timerFired:timer];
}];
+timerWithTimeInterval:repeats:block:
方法与scheduledTimerWithTimeInterval:repeats:block:
类似,但需要手动将计时器添加到RunLoop中。
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0
repeats:YES
block:^(NSTimer * _Nonnull timer) {
[self timerFired:timer];
}];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
使用scheduledTimerWithTimeInterval:
方法创建的计时器会自动启动。如果使用timerWithTimeInterval:
方法创建的计时器,则需要手动将其添加到RunLoop中。
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
要停止计时器,可以调用invalidate
方法。这个方法会停止计时器并将其从RunLoop中移除。
[timer invalidate];
NSTimer
可以设置为重复执行或只执行一次。通过repeats
参数来控制。
repeats: YES
: 计时器会重复执行,直到调用invalidate
方法。repeats: NO
: 计时器只执行一次,执行完毕后会自动停止。NSTimer
的精度受到RunLoop的影响。RunLoop在处理其他事件时,可能会导致计时器的触发时间不准确。因此,NSTimer
不适合用于高精度的计时任务。
NSTimer
依赖于当前线程的RunLoop。如果在一个没有RunLoop的线程中创建NSTimer
,计时器将不会触发。因此,通常在主线程中使用NSTimer
。
如果需要在子线程中使用NSTimer
,需要手动启动RunLoop。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0
target:self
selector:@selector(timerFired:)
userInfo:nil
repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
});
NSTimer
会对target
对象产生强引用,这可能导致内存泄漏。例如,如果在一个ViewController中创建了一个重复执行的计时器,并且计时器的target
是ViewController本身,那么即使ViewController被释放,计时器仍然会持有ViewController的引用,导致ViewController无法被释放。
invalidate
方法释放NSTimer为了避免内存泄漏,需要在适当的时候调用invalidate
方法来停止计时器并释放对target
的强引用。
- (void)dealloc {
[timer invalidate];
}
如果NSTimer
没有触发,可能是以下原因:
target
对象已经被释放。如果NSTimer
设置为重复执行,但没有停止,可能会导致内存泄漏或重复执行不必要的任务。因此,需要在适当的时候调用invalidate
方法。
由于NSTimer
对target
对象有强引用,可能会导致内存泄漏。解决方案是在适当的时候调用invalidate
方法,或者使用weak
引用来避免循环引用。
GCD提供了dispatch_source_t
来实现定时器功能。GCD定时器的精度更高,且不依赖于RunLoop。
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC, 0.1 * NSEC_PER_SEC);
dispatch_source_set_event_handler(timer, ^{
[self timerFired];
});
dispatch_resume(timer);
CADisplayLink
是一个与屏幕刷新率同步的计时器,通常用于动画或与屏幕刷新相关的任务。
CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateDisplay)];
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
NSTimer
是Objective-C中常用的计时器工具,适用于大多数定时任务场景。然而,由于其依赖于RunLoop,精度和线程安全性是需要特别注意的问题。在实际开发中,开发者应根据具体需求选择合适的计时器实现方式,并注意内存管理和计时器的生命周期。
通过本文的介绍,相信读者已经掌握了NSTimer
的基本使用方法、常见问题及其解决方案。希望本文能帮助开发者在iOS开发中更好地使用NSTimer
。
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。
原文链接:https://juejin.cn/post/7215788480542064677