iOS 中处理定时任务的常用方法

栏目: IOS · 发布时间: 5年前

内容简介:级别: ★☆☆☆☆标签:「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。

注意:

  1. iOS10以前初始化的timer在运行期间会对target进行持有,因此,在释放时需要手动调用invalidate方法,并置nil;
  2. timer不能在当前宿主的dealloc方法中调用,因为timer没有被释放前,当前宿主不会执行dealloc方法;
  3. 当前RunLoop会切换Mode,因此可能导致timer不是立刻被触发。
  4. 在同一线程中,timer重复执行期间,有其他耗时任务时,在改耗时任务完成前也不会触发定时,在耗时任务完成后,timer的定时任务会继续执行。
  5. 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(), ^{
        
    });
复制代码

注意:

  1. 正在执行的 block,在调用dispatch_suspend(timer)时,当前block并不会立即停止而是继续执行至完成;
  2. dispatch source在挂起时,直接设置为 nil 或者重新赋值都会造成crash,需要在activate的状态下调用dispatch_source_cancel(timer)后置为 nil 或者重新赋值;
  3. dispatch_source_cancel方法可以在dispatch_source_set_event_handler中调用,即timer可内部持有也可外部持有;
  4. dispatch_resume和dispatch_suspend调用需成对出现,否则会crash;
  5. dispatch source会比 NSTimer 更精准一些。

参考文章,感谢 ...

小编微信:可加并拉入《QiShare技术交流群》。

iOS 中处理定时任务的常用方法

关注我们的途径有:

QiShare(简书)

QiShare(掘金)

QiShare(知乎)

QiShare(GitHub)

QiShare(CocoaChina)

QiShare(StackOverflow)

QiShare(微信公众号)


以上所述就是小编给大家介绍的《iOS 中处理定时任务的常用方法》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Haskell趣学指南

Haskell趣学指南

[斯洛文尼亚] Miran Lipovaca / 李亚舟、宋方睿 / 人民邮电出版社 / 2014-1

《haskell趣学指南》是一本讲解haskell这门函数式编程语言的入门指南,语言通俗易懂,插图生动幽默,示例短小清晰,结构安排合理。书中从haskell的基础知识讲起,涵盖了所有的基本概念和语法,内容涉及基本语法、递归、类型和类型类、函子、applicative 函子、monad、zipper及所有haskell重要特性和强大功能。 《haskell趣学指南》适合对函数式编程及haske......一起来看看 《Haskell趣学指南》 这本书的介绍吧!

URL 编码/解码
URL 编码/解码

URL 编码/解码

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具