博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
iOS使用GCD实现一个Timer
阅读量:5749 次
发布时间:2019-06-18

本文共 4064 字,大约阅读时间需要 13 分钟。

由于 NSTimer 要加到 runloop 中才能工作,这样的话 runloop 在跑圈的时候,如果遇到了当前线程任务比较繁忙,那么它处理 NSTimer 的时机就会滞后,导致 NSTimer 不够准时.因为我们可以用 GCD 的 dispatch_soure_t 去实现一个自己的定时器,而且还比较准时不受 Runloop 影响

YVTimer API 设计 尽可能的仿照 NSTimer

@interface YVTimer : NSObject/** 定时器类方法创建 立即开启 */+ (instancetype)timerWithTimeInterval:(NSTimeInterval)ti							target:(id)aTarget						  selector:(SEL)aSelector						   repeats:(BOOL)yesOrNo;/** 定时器类方法创建 指定开启时间 */+ (instancetype)timerWithFireTime:(NSTimeInterval)start						 interval:(NSTimeInterval)ti						   target:(id)aTarget						 selector:(SEL)aSelector						  repeats:(BOOL)yesOrNo;- (instancetype)init UNAVAILABLE_ATTRIBUTE;+ (instancetype)new UNAVAILABLE_ATTRIBUTE;- (void) fire;- (void) invalidate;@property (readonly) BOOL repeats;@property (readonly) NSTimeInterval timeInterval;@property (readonly, getter=isValid) BOOL valid;@end复制代码

实现代码也非常简单 就是对 GCD dispatch_source_t 的封装

#import "YVTimer.h"#define LOCK  [_lock lock]#define UNLOCK [_lock unlock]@interface YVTimer (){	id _target;	NSTimeInterval _timeInterval;	BOOL _repeats;	dispatch_source_t _timer;	SEL _selector;	BOOL _valid;	NSLock *_lock;}@end@implementation YVTimer+ (instancetype)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector repeats:(BOOL)yesOrNo {	return [YVTimer timerWithFireTime:0.0f interval:ti target:aTarget selector:aSelector repeats:yesOrNo];}+ (instancetype)timerWithFireTime:(NSTimeInterval)start interval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector repeats:(BOOL)yesOrNo {	return [[YVTimer alloc] initWithFireTime:start interval:ti target:aTarget selector:aSelector repeats:yesOrNo];}- (instancetype)initWithFireTime:(NSTimeInterval)start						interval:(NSTimeInterval)interval						  target:(id)target						selector:(SEL)selector						 repeats:(BOOL)repeats {		if (self = [super init]) {		_target = target;		_selector = selector;		_repeats = repeats;		_timeInterval = interval;		_valid = YES;		_lock = [[NSLock alloc] init];				__weak typeof(self)weakSelf = self;		dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,dispatch_get_main_queue());		dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, (start * NSEC_PER_SEC)), (interval * NSEC_PER_SEC), 0);		dispatch_source_set_event_handler(timer, ^{			[weakSelf fire];		});		dispatch_resume(timer);		_timer = timer;	}	return self;}- (void) fire {	if (!_valid) return;#pragma clang diagnostic push#pragma clang diagnostic ignored "-Warc-performSelector-leaks"	LOCK;	id target = _target;	if (!_target) {		[self invalidate];	} else {		[target performSelector:_selector withObject:self];		if (!_repeats) {			[self invalidate];		}	}	UNLOCK;#pragma clang diagnostic pop}- (void)invalidate {	LOCK;	if (_valid) {		dispatch_source_cancel(_timer);		_timer = NULL;		_target = nil;		_valid = NO;	}	UNLOCK;}- (NSTimeInterval)timeInterval {	LOCK;	NSTimeInterval t = _timeInterval;	UNLOCK;	return t;}- (BOOL)repeats {	LOCK;	BOOL r = _repeats;	UNLOCK;	return r;}- (BOOL)isValid {	LOCK;	BOOL valid = _valid;	UNLOCK;	return valid;}- (void)dealloc{	[self invalidate];	NSLog(@"timer dealloc");}@end复制代码

如何使用 建议设置 timer target 时候 不要直接使用当前类 self ,可以通过代理方式 用其他类的对象 去替代 self, 这个代理对象 内部可以弱引用 self ,并通过消息转发 去相应 self要执行的方法

@interface YVProxy : NSProxyNS_ASSUME_NONNULL_BEGIN+ (instancetype) proxyWithTarget:(id)target;- (instancetype)initWithTarget:(id)target;@property (nonatomic, weak, readonly) id target;NS_ASSUME_NONNULL_END@end#import "YVProxy.h"@interface YVProxy ()@end@implementation YVProxy+ (instancetype)proxyWithTarget:(id)target {	return [[YVProxy alloc] initWithTarget:target];}- (instancetype)initWithTarget:(id)target {	_target = target;	return self;}///消息转发 返回方法签名- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {	return [self.target methodSignatureForSelector:sel];}/** 消息转发 将方法交给其他对象去调用 */- (void)forwardInvocation:(NSInvocation *)invocation {	[invocation invokeWithTarget:self.target];}- (void)dealloc{	NSLog(@"YVProxy dealloc");}@end复制代码

好了,我是大兵布莱恩特,欢迎加入博主技术交流群,iOS 开发交流群

转载于:https://juejin.im/post/5b46e523f265da0f793a5b80

你可能感兴趣的文章
java基础(1)
查看>>
ORACLE配置,修改tnsnames.ora文件实例
查看>>
用户无法在输入框中键入数字
查看>>
Workstation服务无法启动导致无法访问文件服务器
查看>>
Gradle:Basic Project
查看>>
.Net组件程序设计之远程调用(二)
查看>>
ant中文教程
查看>>
Linux常用命令(一)
查看>>
安装和使用 Elasticsearch
查看>>
WSUS数据库远端存储条件下切换域及数据库迁移
查看>>
红外遥控资料
查看>>
nginx: client intended to send too large body
查看>>
【VMCloud云平台】SCAP(四)租户(一)
查看>>
python---练习---即时标记
查看>>
linux释放内存的方法
查看>>
基于 Android NDK 的学习之旅----- C调用Java
查看>>
开始第一个Python程序!
查看>>
Google 或强制 OEM 预装 20 款应用,给你一个不Root的理由
查看>>
我的友情链接
查看>>
双边过滤器(Bilateral filter)
查看>>