NSTimmer
方法一
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface IDLTimerProxy : NSObject
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;
@end
NS_ASSUME_NONNULL_END
|
#import "IDLTimerProxy.h"
@interface IDLTimerProxy()
@property(nonatomic, assign, readwrite) SEL selector; @property(nonatomic, weak, readwrite) id target; @property(nonatomic, weak, readwrite) NSTimer *timer;
@end
@implementation IDLTimerProxy
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo { IDLTimerProxy *proxy = [IDLTimerProxy new]; proxy.target = aTarget; proxy.selector = aSelector; proxy.timer = [NSTimer scheduledTimerWithTimeInterval:ti target:proxy selector:@selector(performSelectorWithTimmer:) userInfo:userInfo repeats:yesOrNo]; return proxy.timer; }
- (void)performSelectorWithTimmer:(NSTimer *)timmer { if(self.target && [self.target respondsToSelector:self.selector]) { [self.target performSelector:self.selector withObject:timmer.userInfo]; } else { [self.timer invalidate]; self.timer = nil; } } @end
|
调用方式:
self.timmer = [IDLTimerProxy scheduledTimerWithTimeInterval:5 target:self selector:@selector(run:) userInfo:nil repeats:YES];
|
方法二
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface IDLWeakTimerProxy : NSProxy
+ (instancetype)proxyWithTarget:(id)targe;
@end
NS_ASSUME_NONNULL_END
|
#import "IDLWeakTimerProxy.h"
@interface IDLWeakTimerProxy ()
@property(nonatomic, strong, readwrite) id target;
@end
@implementation IDLWeakTimerProxy
+ (instancetype)proxyWithTarget:(id)target { return [[IDLWeakTimerProxy alloc] initWithTarget:target]; }
- (instancetype)initWithTarget:(id)target { _target = target; return self; }
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel { return [self.target methodSignatureForSelector:sel]; }
- (void)forwardInvocation:(NSInvocation *)invocation { if(!invocation) return; SEL selector = invocation.selector; if([self.target respondsToSelector:selector]) { [invocation invokeWithTarget:self.target]; } }
- (BOOL)respondsToSelector:(SEL)aSelector { return [self.target respondsToSelector:aSelector]; }
@end
|
调用方式:
self.weakTimmerProxy = [IDLWeakTimerProxy proxyWithTarget:self]; self.timmer = [NSTimer scheduledTimerWithTimeInterval:3 target:self.weakTimmerProxy selector:@selector(run:) userInfo:nil repeats:YES];
|
方法三
@implementation NSTimer (IDLBlockTimer)
+ (NSTimer *)idl_scheduledTimerWithTimeInterval:(NSTimeInterval)interval block:(void (^)(void))block repeats:(BOOL)repeats { return [self scheduledTimerWithTimeInterval:interval target:self selector:@selector(idl_blockSelector:) userInfo:[block copy] repeats:repeats]; }
+ (void)idl_blockSelector:(NSTimer *)timer {
void(^block)(void) = timer.userInfo; if (block) { block(); } } @end
|
dispatch_source_t
self.gcdTimmer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)); dispatch_source_set_timer(self.gcdTimmer, dispatch_time(DISPATCH_TIME_NOW, 0), 3 * NSEC_PER_SEC, 0); dispatch_source_set_event_handler(self.gcdTimmer, ^{ }); dispatch_resume(self.gcdTimmer);
|
CADisplayLink
- CADisplayLink 与 NSTimer 的区别:
CADisplayLink是一个能让我们以和屏幕刷新率同步的频率将特定的内容绘制到屏幕上的定时器类。CADisplayLink以特定模式注册到runloop后,每当屏幕显示内容刷新结束的时候,runloop就会向CADisplayLink指定的target发送一次指定的selector消息,CADisplayLink类对应的selector就会被调用一次。
NSTimer以指定的模式注册到runloop后,每当设定的周期时间到达后,runloop会向指定的target发送一次指定的selector消息。
对于精度而言:
由于iOS设备的屏幕刷新频率是固定的,CADisplayLink在正常情况下精确度相当高。NSTimer的精确度就显得低了点,比如NSTimer的触发时间到的时候,runloop如果在忙于别的调用,触发时间就会推迟到下一个runloop周期。NSTimer新增了tolerance属性,让用户可以设置可以容忍的触发的时间范围。
CADisplayLink使用场合相对专一,适合做界面的不停重绘。而NSTimer的使用范围要广泛的多,各种需要单次或者循环定时处理的任务都可以使用。
例子:
self.weakTimmerProxy = [IDLWeakTimerProxy proxyWithTarget:self]; self.cadisplaylink = [CADisplayLink displayLinkWithTarget:self.weakTimmerProxy selector:@selector(run:)]; [self.cadisplaylink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; self.cadisplaylink.paused = YES;
|
CADisplayLink 接口:
@interface CADisplayLink : NSObject { @private void *_impl; }
@property(nonatomic) NSInteger preferredFramesPerSecond API_AVAILABLE(ios(10.0), watchos(3.0), tvos(10.0));
+ (CADisplayLink *)displayLinkWithTarget:(id)target selector:(SEL)sel;
- (void)addToRunLoop:(NSRunLoop *)runloop forMode:(NSRunLoopMode)mode;
- (void)removeFromRunLoop:(NSRunLoop *)runloop forMode:(NSRunLoopMode)mode;
- (void)invalidate;
@property(getter=isPaused, nonatomic) BOOL paused;
@property(readonly, nonatomic) CFTimeInterval timestamp; @property(readonly, nonatomic) CFTimeInterval duration; @property(readonly, nonatomic) CFTimeInterval targetTimestamp API_AVAILABLE(ios(10.0), watchos(3.0), tvos(10.0)); @end
|