iOS使用GCD实现一个Timer

由于 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 : NSProxy

NS_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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值