全局秒定时器

@全局秒定时器

全局秒定时器

在我们的项目实践中,往往会用到周期为1秒的定时器。此外,我们用到的定时器周期,一般都是整数秒的。在复杂项目中,同一时刻这种定时器可能会开启很多个。这就导致了系统额外的开销。我们在想,对于这种整数秒周期的定时器,我们是否可以合并一下?这样既可以简化代码逻辑,又可以减少性能消耗。
于是我编写了一个全局定时器,用于周期是整数秒、精度要求不是很高的定时任务场合。

思路:

首先我们建立一个单例,我们称他为SecondTrigger,意为秒触发器。单例里面保存了一个NSTimer定时器。然后我们再建立一个可变数组,用于保存想要接受定时回调的target。当可变数组数量不为0时,定时器开启。可变数组内的target可以通过接口增加或减少。当定时器发现可变数组数量为0时,关闭定时器。

接口:

- (BOOL)addDelegate:(id<KKSecondTriggerDelegate>)delegate;

用于增加需要接受时间回调的target,增加后,target每一秒会接收到回调,target需要实现KKSecondTriggerDelegate协议,回调函数的原型就是在KKSecondTriggerDelegate总声明的

回调原形

@protocol KKSecondTriggerDelegate
@optional
- (void)secondTrigger:(KKSecondTrigger *)secondTrigger
              counter:(NSInteger)counter;

@end

回调函数的原形就是在KKSecondTriggerDelegate里声明的。参数里除了秒触发器指针外,还有counter参数,表示这是第几次回调。当我们的定时器周期需要n秒出发一次的时候,我们只需要在回调函数里将 counter模上n,结果不为0就return即可。此外,counter可能还有其他一些用途。

扩展:

为了增加实用性,我们还增设了以下接口,用于参数的带入。

- (void)secondTrigger:(KKSecondTrigger *)secondTrigger
              counter:(NSInteger)counter
           parameter1:(id __nullable)parameter1
           parameter2:(id __nullable)parameter2;
- (void)secondTrigger:(KKSecondTrigger *)secondTrigger
              counter:(NSInteger)counter
           parameter1:(id __nullable)parameter1;

代理协议里面增加了以下接口,用于参数的带出:

- (void)secondTrigger:(KKSecondTrigger *)secondTrigger
              counter:(NSInteger)counter
           parameter1:(id __nullable)parameter1
           parameter2:(id __nullable)parameter2;
- (void)secondTrigger:(KKSecondTrigger *)secondTrigger
              counter:(NSInteger)counter
           parameter1:(id __nullable)parameter1;

以上接口只有参数个数不同,其他方面功能一样。便于用户在定时器里带入带出一些参数。如果调用的接口是n个参数的,那么n个参数的回调会被调用。n可以等于0、1、2。

其他接口:

1、由于定时器在开启以后,不会立刻触发,而是要等打破下一个周期才会触发。为了满处用户添加额外的定时器触发,我们还增开了fire接口,用于手动、额外的定时器触发
2、当不需要定时器触发的时候,我们可以调用removeDelegate来取消回调。

优点:

1、对于用途很广的秒周期的定时器,全局最多开启一个定时器,节约了资源
2、如果用户忘记调用removeDelegate,定时器会在target为nil的时候,自动关闭定时器。注意:定时器对target是若易用的
3、已经考虑了用户拖拽scrollView1是导致定时器不触发的情况

最终代码:

头文件:

#import <Foundation/Foundation.h>
@class KKSecondTrigger;

NS_ASSUME_NONNULL_BEGIN

@protocol KKSecondTriggerDelegate

@optional
- (void)secondTrigger:(KKSecondTrigger *)secondTrigger
			  counter:(NSInteger)counter
		   parameter1:(id __nullable)parameter1
		   parameter2:(id __nullable)parameter2;
- (void)secondTrigger:(KKSecondTrigger *)secondTrigger
			  counter:(NSInteger)counter
		   parameter1:(id __nullable)parameter1;
- (void)secondTrigger:(KKSecondTrigger *)secondTrigger
			  counter:(NSInteger)counter;

@end

@interface KKSecondTrigger : NSObject

+ (instancetype)sharedTrigger;

- (BOOL)addDelegate:(id<KKSecondTriggerDelegate>)delegate
		 parameter1:(id __nullable)parameter1
		 parameter2:(id __nullable)parameter2;

- (BOOL)addDelegate:(id<KKSecondTriggerDelegate>)delegate
		 parameter1:(id __nullable)parameter1;

- (BOOL)addDelegate:(id<KKSecondTriggerDelegate>)delegate;

- (BOOL)pause:(id<KKSecondTriggerDelegate>)delegate tag:(BOOL)tag;

- (void)resetSecondCount:(id<KKSecondTriggerDelegate>)delegate;

- (BOOL)removeDelegate:(id<KKSecondTriggerDelegate>)delegate;

// 默认是一秒触发一次,若需要手动触发,请调用这个方法
- (BOOL)triggerDelegate:(id<KKSecondTriggerDelegate>)delegate;

- (BOOL)isTriggering:(id<KKSecondTriggerDelegate>)delegate;

@end

NS_ASSUME_NONNULL_END

m文件:


#import "KKSecondTrigger.h"

@interface KKSecondTriggerElement : NSObject
@property (nonatomic, assign) NSInteger parameterCount;
@property (nonatomic, strong) id parameter1;
@property (nonatomic, strong) id parameter2;
@property (nonatomic, weak) id<KKSecondTriggerDelegate> delegate;
@property (nonatomic, assign) NSInteger count;
@property (nonatomic, assign) BOOL paused;

@end

@implementation KKSecondTriggerElement

@end

@implementation KKSecondTrigger {
	NSTimer* _theTimer;
	// 用于存储KKSecondTriggerElement
	NSMutableDictionary *_allElements;
}

+ (instancetype)sharedTrigger {
	static id trigger = nil;
	static dispatch_once_t onceToken;
	dispatch_once(&onceToken, ^{
		trigger = [[self alloc] init];
	});
	return trigger;
}

- (BOOL)addDelegate:(id<KKSecondTriggerDelegate>)delegate
		 parameter1:(id)parameter1
		 parameter2:(id)parameter2 {
	[self verifyValidityWhenAddDelegate:delegate];
	NSString* stringKey = [NSString stringWithFormat:@"%p", delegate];
	
	KKSecondTriggerElement* element = [[KKSecondTriggerElement alloc] init];
	element.parameterCount = 2;
	element.delegate = delegate;
	element.parameter1 = parameter1;
	element.parameter2 = parameter2;
	element.count = 0;
	element.paused = NO;
	_allElements[stringKey] = element;
	return YES;
}

- (BOOL)addDelegate:(id<KKSecondTriggerDelegate>)delegate
		 parameter1:(id)parameter1 {
	[self verifyValidityWhenAddDelegate:delegate];
	NSString *stringKey = [NSString stringWithFormat:@"%p", delegate];
	
	KKSecondTriggerElement *element = [[KKSecondTriggerElement alloc] init];
	element.parameterCount = 1;
	element.delegate = delegate;
	element.parameter1 = parameter1;
	element.parameter2 = nil;
	element.count = 0;
	element.paused = NO;
	_allElements[stringKey] = element;
	return YES;
}

- (BOOL)addDelegate:(id<KKSecondTriggerDelegate>)delegate {
	[self verifyValidityWhenAddDelegate:delegate];
	NSString* stringKey = [NSString stringWithFormat:@"%p", delegate];
	
	KKSecondTriggerElement* element = [[KKSecondTriggerElement alloc] init];
	element.parameterCount = 0;
	element.delegate = delegate;
	element.parameter1 = nil;
	element.parameter2 = nil;
	element.count = 0;
	element.paused = NO;
	_allElements[stringKey] = element;
	return YES;
}

- (BOOL)triggerDelegate:(id<KKSecondTriggerDelegate>)delegate
{
	KKSecondTriggerElement *aElement = [self getElementWithDelegate:delegate];
	if (aElement.delegate)
	{
		return [self triggerElement:aElement];
	}
	return YES;
}

- (BOOL)isTriggering:(id<KKSecondTriggerDelegate>)delegate
{
	KKSecondTriggerElement *aElement = [self getElementWithDelegate:delegate];
	return aElement != nil;
}

- (BOOL)pause:(id<KKSecondTriggerDelegate>)delegate tag:(BOOL)tag
{
	KKSecondTriggerElement *aElement = [self getElementWithDelegate:delegate];
	if (aElement.delegate)
	{
		aElement.paused = tag;
	}
	return YES;
}

- (BOOL)removeDelegate:(id<KKSecondTriggerDelegate>)delegate
{
	NSString *stringKey = [NSString stringWithFormat:@"%p", delegate];
	if (_allElements && _allElements[stringKey])
	{
		[_allElements removeObjectForKey:stringKey];
		return YES;
	}

	return NO;
}

- (void)resetSecondCount:(id<KKSecondTriggerDelegate>)delegate
{
	KKSecondTriggerElement *aElement = [self getElementWithDelegate:delegate];
	aElement.count = 0;
}

#pragma mark - private

- (void)onSecondTrigger
{
	[_allElements.allKeys enumerateObjectsUsingBlock:^(NSString * _Nonnull keyPilot, NSUInteger idx, BOOL * _Nonnull stop) {
		KKSecondTriggerElement *aElement = _allElements[keyPilot];
		if (aElement.delegate) {
			[self triggerElement:aElement];
		} else {
			[_allElements removeObjectForKey:keyPilot];
		}
	}];
	
	if (_allElements.count == 0) {
		_allElements = nil;
		if (_theTimer) {
			[_theTimer invalidate];
			_theTimer = nil;
		}
	}
}

- (BOOL)triggerElement:(KKSecondTriggerElement*)element
{
	if (element.paused) {
		return NO;
	}
	element.count++;
	id delegate = element.delegate;
	if (element.parameterCount == 0) {
		if ([delegate respondsToSelector:@selector(secondTrigger:counter:)]) {
			[delegate secondTrigger:self counter:element.count];
			return YES;
		}
	} else if (element.parameterCount == 1) {
		if ([delegate respondsToSelector:@selector(secondTrigger:counter:parameter1:)]) {
			[delegate secondTrigger:self counter:element.count parameter1:element.parameter1];
			return YES;
		}
	} else if (element.parameterCount == 2) {
		if ([delegate respondsToSelector:@selector(secondTrigger:counter:parameter1:parameter2:)]) {
			[delegate secondTrigger:self counter:element.count parameter1:element.parameter1 parameter2:element.parameter2];
			return YES;
		}
	}
	return NO;
}

- (BOOL)verifyValidityWhenAddDelegate:(id<KKSecondTriggerDelegate>)delegate
{
	if (delegate == nil) {
		return NO;
	}
	
	if (_allElements == nil) {
		_allElements = [[NSMutableDictionary alloc] init];
	}
	
	if ([self getElementWithDelegate:delegate].delegate) {
		return NO;
	}
	
	if (_theTimer == nil)
    {
        _theTimer = [Tools startTimerWithInterval:1.0 target:self action:@selector(onSecondTrigger) userInfo:nil repeats:YES];
	}
	return YES;
}

- (KKSecondTriggerElement *)getElementWithDelegate:(id<KKSecondTriggerDelegate>)delegate
{
	NSString *stringKey = [NSString stringWithFormat:@"%p", delegate];
	return _allElements[stringKey];
}

@end

开启NSTimer的代码

@implementation Tools
+ (NSTimer*)startTimerWithInterval:(NSTimeInterval)ti
                            target:(id)aTarget
                            action:(SEL)action
                          userInfo:(id __nullable)userInfo
                           repeats:(BOOL)yesOrNo
{
    NSString* strDebug = [NSString stringWithFormat:@"timeInterval = %f, target = %@, action = %@, userInfo = %@, repeats = %d", ti, aTarget, NSStringFromSelector(action), userInfo, yesOrNo];
    NSTimer* timer = [NSTimer scheduledTimerWithTimeInterval:ti target:[[KKTimer alloc]initWithTarget:aTarget andAction:action andDebugString:strDebug] selector:@selector(timerCallBack:) userInfo:userInfo repeats:yesOrNo];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
    return timer;
}
@endif
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值