我们在项目开发过程中有时会遇到这种需求:
- tableview等列表中会含有多个倒计时的UI
- 在启动某个操作就需要定时器重复执行某个动作(比如上传地理坐标,心跳)
我的做法是开启个全局定时器,有对象要计时的时候调用addNotificationObserver:(id)observer type:(MDGlobalTimerType)type selector:(SEL)seletor
把注册到MDGlobalTimer类里,
在对象delloc或者释放的时候调用removeNotificationObserver:(id)observer type:(MDGlobalTimerType)type
方法注销对象
然后在add的方法中开启一个定时器(已开启就不开了)
在remove方法中判断数组里还有对象没,没有的话表明没有需要计时的对象,就停止定时器并销毁
计时:每过一定时间(这里设置成1s),发出通知(add方法里已经注册了通知),然后执行对象的方法,进行刷新UI
- (void)addNotificationObserver:(id)observer type:(MDGlobalTimerType)type selector:(SEL)seletor{
BOOL addNotification = NO;
NSString *notificationName = nil;
switch (type) {
case MDGlobalTimerTypeOneSecond:
{
NSMutableSet *secondsObservers = [NSMutableSet setWithSet:_observers[kOneSecond]];
if (![secondsObservers containsObject:observer]) {
[secondsObservers addObject:observer];
addNotification = YES;
notificationName = notification_kOneSecond;
_observers[kOneSecond] = secondsObservers;
}
}
break;
case MDGlobalTimerTypeHalfMinute:
{
NSMutableSet *halfMinuteObservers = [NSMutableSet setWithSet:_observers[kHalfMinute]];
if (![halfMinuteObservers containsObject:observer]) {
[halfMinuteObservers addObject:observer];
addNotification = YES;
notificationName = notification_kHalfMinute;
_observers[kHalfMinute] = halfMinuteObservers;
}
}
break;
case MDGlobalTimerTypeOneMinute:
{
NSMutableSet *oneMinuteObservers = [NSMutableSet setWithSet:_observers[kOneMinute]];
if (![oneMinuteObservers containsObject:observer]) {
[oneMinuteObservers addObject:observer];
addNotification = YES;
notificationName = notification_kOneMinute;
_observers[kOneMinute] = oneMinuteObservers;
}
}
break;
default:
break;
}
//添加通知
if (addNotification) {
[[NSNotificationCenter defaultCenter] addObserver:observer selector:seletor name:notificationName object:nil];
}
//开启定时器
[self startTiming];
}
- (void)removeNotificationObserver:(id)observer type:(MDGlobalTimerType)type{
NSString *notificationName = nil;
switch (type) {
case MDGlobalTimerTypeOneSecond:
{
NSMutableSet *secondsObservers = _observers[kOneSecond];
[secondsObservers removeObject:observer];
_observers[kOneSecond] = secondsObservers;
notificationName = notification_kOneSecond;
}
break;
case MDGlobalTimerTypeHalfMinute:
{
NSMutableSet *halfMinuteObservers = _observers[kHalfMinute];
[halfMinuteObservers removeObject:observer];
_observers[kHalfMinute] = halfMinuteObservers;
notificationName = notification_kHalfMinute;
}
break;
case MDGlobalTimerTypeOneMinute:
{
NSMutableSet *oneMinuteObservers = _observers[kOneSecond];
[oneMinuteObservers removeObject:observer];
_observers[kOneSecond] = oneMinuteObservers;
notificationName = notification_kOneMinute;
}
break;
default:
break;
}
//移除监听
[[NSNotificationCenter defaultCenter] removeObserver:observer name:notificationName object:nil];
//移除空set
[_observers enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSMutableSet * _Nonnull obj, BOOL * _Nonnull stop) {
if (obj.count == 0) {
[_observers removeObjectForKey:key];
}
}];
//关闭定时器
[self stopTiming];
}
启动定时器:
static NSString *const kOneSecond = @"kOneSecond";
static NSString *const kHalfMinute = @"kHalfMinute";
static NSString *const kOneMinute = @"kOneMinute";
@implementation MDGlobalTimer{
dispatch_source_t _timer;
int _seconds;
NSMutableDictionary <NSString *,NSMutableSet*> *_observers;
}
+ (id)allocWithZone:(struct _NSZone *)zone{
return [self sharedInstance];
}
+ (instancetype)sharedInstance{
static MDGlobalTimer *timer;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
timer = [[super allocWithZone:nil] init];
timer->_observers = @{}.mutableCopy;
});
return timer;
}
/** 开启定时器 */
- (void)startTiming{
if (_timer) {
return;
}
_seconds = 0;
_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));
dispatch_source_set_timer(_timer, DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
dispatch_source_set_event_handler(_timer, ^{
_seconds++;
dispatch_sync(dispatch_get_main_queue(), ^{
//发送s
if (_observers[kOneSecond].count > 0) {
[[NSNotificationCenter defaultCenter] postNotificationName:notification_kOneSecond object:nil];
}
//发送半分钟通知
if (_seconds%30 == 0 && _observers[kHalfMinute].count > 0) {
[[NSNotificationCenter defaultCenter] postNotificationName:notification_kHalfMinute object:nil];
}
//发送1分钟通知
if (_seconds%60 == 0 && _observers[kOneMinute].count > 0) {
[[NSNotificationCenter defaultCenter] postNotificationName:notification_kOneMinute object:nil];
_seconds = 0;
}
});
});
dispatch_resume(_timer);
}
/** 结束定时器 */
- (void)stopTiming{
if (_timer && _observers.count == 0) {
dispatch_source_cancel(_timer);
_timer = nil;
}
}
这里由于工程中只有分、秒、30s的定时需求,所以只写死了这三个,如果有更细节的定时,可以在add方法里加计时间隔,并在计时时以最大公因数判断
如果有什么不对的地方,希望评论指正下œ∑®
MDGlobalTimer-demo
网友评论