方法一:直接使用 scheduledTimerWithTimeInterval:repeats:block:
的方法
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
NSLog(@"%.2f", CACurrentMediaTime());
}];
方法二:通过设置一个中间类(以下提供两种方法)
//.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface MZObjectProxy : NSObject
+ (instancetype)proxyWithTarget:(id)target;
@property (nonatomic, weak) id target;
@end
@interface MZProxy : NSProxy
+ (instancetype)proxyWithTarget:(id)target;
@property (nonatomic, weak) id target;
@end
NS_ASSUME_NONNULL_END
//.m
#import "MZProxy.h"
@implementation MZObjectProxy
+ (instancetype)proxyWithTarget:(id)target {
MZObjectProxy *proxy = [[MZObjectProxy alloc] init];
proxy.target = target;
return proxy;
}
- (id)forwardingTargetForSelector:(SEL)aSelector {
return _target;
}
@end
@implementation MZProxy
+ (instancetype)proxyWithTarget:(id)target {
MZProxy *proxy = [MZProxy alloc];
proxy.target = target;
return proxy;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
return [self.target methodSignatureForSelector:sel];
}
- (void)forwardInvocation:(NSInvocation *)invocation {
[invocation invokeWithTarget:self.target];
}
@end
附上通过GCD创建的Timer
NSTimer依赖于NSRunLoop,当runloop的任务繁重时,就会导致NSTimer计时不准确。使用GCD的timer,能够保证计时精确。
//MZGCDTimer.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface MZGCDTimer : NSObject
/// 用GCD创建Timer,block执行,返回任务的id
/// @param delay 延迟执行的秒数
/// @param interval 间隔时间
/// @param repeats 是否重复
/// @param async 在主线程还是子线程执行
/// @param task 任务block
+ (NSString *)timeWithDelay:(NSTimeInterval)delay interval:(NSTimeInterval)interval repeats:(BOOL)repeats async:(BOOL)async execTask:(void(^)(void))task;
/// 用GCD创建Timer,selector执行,返回任务的id
/// @param target 执行selector的对象
/// @param selector selector
/// @param delay 延迟执行的秒数
/// @param interval 间隔时间
/// @param repeats 是否重复
/// @param async 在主线程还是子线程执行
+ (NSString *)timeWithTarget:(id)target selector:(SEL)selector delay:(NSTimeInterval)delay interval:(NSTimeInterval)interval repeats:(BOOL)repeats async:(BOOL)async;
/// 通过任务id,取消任务
/// @param name 任务id
+ (void)cancelTask:(NSString *)name;
@end
NS_ASSUME_NONNULL_END
//MZGCDTimer.m
#import "MZGCDTimer.h"
@interface MZGCDTimerProxy : NSProxy
+ (instancetype)proxyWithTarget:(id)target;
@property (nonatomic, weak) id target;
@end
static NSMutableDictionary *timerDict_;
static dispatch_semaphore_t semaphore_;
@implementation MZGCDTimer
+ (void)initialize {
[super initialize];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
timerDict_ = [NSMutableDictionary dictionary];
semaphore_ = dispatch_semaphore_create(1);
});
}
+ (NSString *)timeWithDelay:(NSTimeInterval)delay interval:(NSTimeInterval)interval repeats:(BOOL)repeats async:(BOOL)async execTask:(void (^)(void))task {
if (!task || (interval <= 0 && repeats)) return nil;
delay = MAX(0, delay);
dispatch_queue_t queue = async ? dispatch_get_global_queue(0, 0) : dispatch_get_main_queue();
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, delay * NSEC_PER_SEC), interval * NSEC_PER_SEC, 0);
//同步
dispatch_semaphore_wait(semaphore_, DISPATCH_TIME_FOREVER);
NSString *name = [NSString stringWithFormat:@"%zd", timerDict_.count];
timerDict_[name] = timer;
dispatch_semaphore_signal(semaphore_);
dispatch_source_set_event_handler(timer, ^{
task();
if (!repeats) {
[self cancelTask:name];
}
});
dispatch_resume(timer);
return name;
}
+ (NSString *)timeWithTarget:(id)target selector:(SEL)selector delay:(NSTimeInterval)delay interval:(NSTimeInterval)interval repeats:(BOOL)repeats async:(BOOL)async {
if (!target || !selector) return nil;
target = [MZGCDTimerProxy proxyWithTarget:target];
NSString *name = [self timeWithDelay:delay interval:interval repeats:repeats async:async execTask:^{
if ([target respondsToSelector:selector]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[target performSelector:selector];
#pragma clang diagnostic pop
}
}];
return name;
}
+ (void)cancelTask:(NSString *)name {
if (name.length == 0) return;
dispatch_semaphore_wait(semaphore_, DISPATCH_TIME_FOREVER);
dispatch_source_t timer = timerDict_[name];
if (timer) {
dispatch_source_cancel(timer);
[timerDict_ removeObjectForKey:name];
}
dispatch_semaphore_signal(semaphore_);
}
@end
@implementation MZGCDTimerProxy
+ (instancetype)proxyWithTarget:(id)target {
MZGCDTimerProxy *proxy = [MZGCDTimerProxy alloc];
proxy.target = target;
return proxy;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
return [self.target methodSignatureForSelector:sel];
}
- (void)forwardInvocation:(NSInvocation *)invocation {
[invocation invokeWithTarget:self.target];
}
@end