iOS常用定时器有3种
- NSTimer
- GCD定时器 dispatch_source_t
- CADisplayLink 与屏幕刷新率同步的通知
精准度
- NSTimer因为runloop的运行机制,会存在延时
- CADisplayLink在页面掉帧的情况下会不太准,不掉帧的情况下比较精准
- GCD定时器比NSTimer更精准
各种定时器的使用
// 创建
// NSTimer创建方式1(此种方式需要手动添加到runloop才开始执行)
NSTimer *_timer1 = [NSTimer timerWithTimeInterval:3 target:self selector:@selector(timer1) userInfo:nil repeats:YES];
/**********
手动添加到runloop,为了保证timer一定执行需要添加到NSRunLoopCommonModes下,
该模式中的source和timer会拷贝到所有其他的模式下。
如果加在NSDefaultRunLoopMode,在UIScrollView拖动的时候timer不会执行,
因为在UIScrollView拖动拖动的时候住线程自动被系统切换到UITrackingRunLoopMode.
****/
[[NSRunLoop mainRunLoop] addTimer:_timer1 forMode:NSRunLoopCommonModes];
// NSTimer创建方式2 (此种方式不用手动添加runloop,立即开始执行)
_timer2 = [NSTimer scheduledTimerWithTimeInterval:3 target:self selector:@selector(timer2) userInfo:nil repeats:YES];
// CADisplayLink
CADisplayLink *_displayLink = [CADisplayLink displayLinkWithTarget:wapper selector:@selector(displayLink)];
_displayLink.frameInterval = 60; // 1s执行一次,不掉帧的刷新频率60次每秒
[_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
// gcd timer
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_source_set_timer(_timer, DISPATCH_TIME_NOW, 3.0 * NSEC_PER_SEC, 0);
// __weak typeof(self) weakSelf = self;
dispatch_source_set_event_handler(_timer, ^{
// DO balabalabala...
});
// 开启定时器
dispatch_resume(_timer);
// 销毁
// NSTimer
[_timer1 invalidate];
[_timer2 invalidate];
// CADIsplayLink
[_displayLink invalidate];
// GCD
dispatch_source_cancel(_timer);
关于内存泄漏
正常情况下Timer的生命周期必然比其Target小,所以这里只要想办法将Timer对Target的强引用转换成弱引用,然后我们就能在Target的delloc方法中执行Timer的销毁方法。
** 利用runtime我设计了一个处理方式 **
// WeakWrapper.h
#import <Foundation/Foundation.h>
@interface WeakWrapper : NSObject
- (instancetype)initWith:(id)willWeak;
@end
// WeakWrapper.m
#import "WeakWrapper.h"
#import <objc/runtime.h>
@interface WeakWrapper()
@property (nonatomic, weak) id weakObj;
@end
@implementation WeakWrapper
- (instancetype)initWith:(id)willWeak {
if ([super init]) {
self.weakObj = willWeak;
}
return self;
}
- (instancetype)init {
[self doesNotRecognizeSelector:_cmd];
return nil;
}
// 消息转发
- (id)forwardingTargetForSelector:(SEL)aSelector {
return _weakObj;
}
@end
// 使用
WeakWrapper *wapper = [[WeakWrapper alloc] initWith:self];
_timer1 = [NSTimer timerWithTimeInterval:3 target: wapper selector:@selector(doTimer) userInfo:nil repeats:YES];
代码封装
针对iOS的Timer我封装了一个类库 GTM_Timer
简介:
- 支持用block创建各类Timer(NSTimer, GCD timer, CADisplayLink)
- 解决了Timer的保留环造成的内存泄漏的问题