内容简介:级别: ★☆☆☆☆标签:「iOS」「定时任务 」作者:dac_1033
级别: ★☆☆☆☆
标签:「iOS」「定时任务 」
作者:dac_1033
审校:QiShare团队
在项目开发中,经常会在代码中处理一些需要延时或定时执行的任务,iOS 中处理定时任务的方法包括 performSelector 方法、NSTimer、GCD、CADisplayLink,其本质都是通过RunLoop来实现,下面我们就对这几个方法做一些总结。
1. performSelector方法
在NSRunLoop.h中有对NSObject类的扩展方法,简单易用:
@interface NSObject (NSDelayedPerforming) - (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray<NSRunLoopMode> *)modes; - (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay; + (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget selector:(SEL)aSelector object:(nullable id)anArgument; + (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget; @end 复制代码
2. NSTimer
NSTimer 是最常使用的定时器,使用方式简单,NSTimer 是也通过添加到RunLoop中被触发并进行工作的,桥接 CFRunLoopTimerRef。NSTimer中定义的常用方法如下:
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo; + (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo; + (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo; + (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo; + (NSTimer *)timerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0)); + (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0)); - (instancetype)initWithFireDate:(NSDate *)date interval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0)); - (instancetype)initWithFireDate:(NSDate *)date interval:(NSTimeInterval)ti target:(id)t selector:(SEL)s userInfo:(nullable id)ui repeats:(BOOL)rep NS_DESIGNATED_INITIALIZER; 复制代码
以下是初始化NSTimer的不同方式:
// 自动加入currentRunLoop self.timer = [NSTimer scheduledTimerWithTimeInterval:5 target:self selector:@selector(timerRuning) userInfo:nil repeats:YES]; //self.timer = [NSTimer scheduledTimerWithTimeInterval:5.0 repeats:YES block:^(NSTimer * _Nonnull timer) { }]; // 手动加入RunLoop self.timer = [NSTimer timerWithTimeInterval:5 target:self selector:@selector(timerRuning) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode]; // 指定timer触发时刻 NSTimeInterval timeInterval = [self timeIntervalSinceReferenceDate] + 30; NSDate *newDate = [NSDate dateWithTimeIntervalSinceReferenceDate:timeInterval]; self.timer = [[NSTimer alloc] initWithFireDate:newDate interval:5 target:self selector:@selector(timerRuning) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode]; 复制代码
如果当前界面中有UITableView,则在 UITableView 在滚动过程中,上述代码中的定时器到了时间并没有触发。根据RunLoop的相关知识,同一时刻 RunLoop 只运行在一种 Mode 上,并且只有这个 Mode 相关联的源或定时器会被传递消息,mainRunLoop 一般处于 NSDefaultRunLoopMode,但是在滚动或者点击事件等触发时,mainRunLoop 切换至 NSEventTrackingRunLoopMode ,而上面 timer 被加入的正是 NSDefaultRunLoopMode (未指明也默认加入默认模式),所以滑动时未触发定时操作。 解决方法:添加timer到mainRunLoop的NSRunLoopCommonMode中或者子线程中,需要注意的是加入子线程时要手动开启并运行子线程的RunLoop。
self.timer = [NSTimer scheduledTimerWithTimeInterval:5 target:self selector:@selector(timerRuning) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes]; 复制代码
NSRunLoopCommonMode这是一组可配置的常用模式。将输入源与此模式相关联也会将其与组中的每个模式相关联。对于Cocoa应用程序,此集合默认包括NSDefaultRunLoopMode,NSPanelRunLoopMode和NSEventTrackingRunLoopMode。
注意:
- iOS10以前初始化的timer在运行期间会对target进行持有,因此,在释放时需要手动调用invalidate方法,并置nil;
- timer不能在当前宿主的dealloc方法中调用,因为timer没有被释放前,当前宿主不会执行dealloc方法;
- 当前RunLoop会切换Mode,因此可能导致timer不是立刻被触发。
- 在同一线程中,timer重复执行期间,有其他耗时任务时,在改耗时任务完成前也不会触发定时,在耗时任务完成后,timer的定时任务会继续执行。
- dispatch_source_set_timer中设置启动时间,dispatch_time_t可通过两个方法生成:dispatch_time 和 dispatch_walltime
3. GCD定时器
我们也可以通过GCD中的方法实现定时器来处理定时任务,实现的代码逻辑如下:
// 1. 创建 dispatch source,指定检测事件为定时 dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue("Timer_Queue", 0)); // 2. 设置定时器启动时间、间隔 dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 0.5 * NSEC_PER_SEC, 0 * NSEC_PER_SEC); // 3. 设置callback dispatch_source_set_event_handler(timer, ^{ NSLog(@"timer fired"); }); dispatch_source_set_event_handler(timer, ^{ //取消定时器时一些操作 }); // 4. 启动定时器(刚创建的source处于被挂起状态) dispatch_resume(timer); // 5. 暂停定时器 dispatch_suspend(timer); // 6. 取消定时器 dispatch_source_cancel(timer); timer = nil; 复制代码
当我们想要timer只是延时执行一次时,只调用以下方法即可:
// 在主线程中延时5s中执行 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ }); 复制代码
注意:
- 正在执行的 block,在调用dispatch_suspend(timer)时,当前block并不会立即停止而是继续执行至完成;
- dispatch source在挂起时,直接设置为 nil 或者重新赋值都会造成crash,需要在activate的状态下调用dispatch_source_cancel(timer)后置为 nil 或者重新赋值;
- dispatch_source_cancel方法可以在dispatch_source_set_event_handler中调用,即timer可内部持有也可外部持有;
- dispatch_resume和dispatch_suspend调用需成对出现,否则会crash;
- dispatch source会比 NSTimer 更精准一些。
参考文章,感谢 ...
小编微信:可加并拉入《QiShare技术交流群》。
关注我们的途径有:
QiShare(微信公众号)
以上所述就是小编给大家介绍的《iOS 中处理定时任务的常用方法》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- PyVideoResearch:视频研究常用方法、数据集和任务汇总
- SpringBoot与异步任务、定时任务、邮件任务
- 订阅 + 定时任务重构后台主机操作任务
- 宏任务和微任务的一个小事
- Yii2 定时任务创建(Console 任务)
- Yii2 定时任务创建(Console 任务)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Haskell趣学指南
[斯洛文尼亚] Miran Lipovaca / 李亚舟、宋方睿 / 人民邮电出版社 / 2014-1
《haskell趣学指南》是一本讲解haskell这门函数式编程语言的入门指南,语言通俗易懂,插图生动幽默,示例短小清晰,结构安排合理。书中从haskell的基础知识讲起,涵盖了所有的基本概念和语法,内容涉及基本语法、递归、类型和类型类、函子、applicative 函子、monad、zipper及所有haskell重要特性和强大功能。 《haskell趣学指南》适合对函数式编程及haske......一起来看看 《Haskell趣学指南》 这本书的介绍吧!