iOS 定时器

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);

关于内存泄漏

v

正常情况下Timer的生命周期必然比其Target小,所以这里只要想办法将Timer对Target的强引用转换成弱引用,然后我们就能在Target的delloc方法中执行Timer的销毁方法。

** 利用runtime我设计了一个处理方式 **

// WeakWrapper.h
#import 
@interface WeakWrapper : NSObject
- (instancetype)initWith:(id)willWeak;
@end

// WeakWrapper.m
#import "WeakWrapper.h"
#import 

@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的保留环造成的内存泄漏的问题

你可能感兴趣的:(iOS 定时器)