方法一:直接使用 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創(chuàng)建的Timer
NSTimer依賴于NSRunLoop,當runloop的任務繁重時肿男,就會導致NSTimer計時不準確薛夜。使用GCD的timer抵蚊,能夠保證計時精確事扭。
//MZGCDTimer.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface MZGCDTimer : NSObject
/// 用GCD創(chuàng)建Timer,block執(zhí)行,返回任務的id
/// @param delay 延遲執(zhí)行的秒數(shù)
/// @param interval 間隔時間
/// @param repeats 是否重復
/// @param async 在主線程還是子線程執(zhí)行
/// @param task 任務block
+ (NSString *)timeWithDelay:(NSTimeInterval)delay interval:(NSTimeInterval)interval repeats:(BOOL)repeats async:(BOOL)async execTask:(void(^)(void))task;
/// 用GCD創(chuàng)建Timer,selector執(zhí)行,返回任務的id
/// @param target 執(zhí)行selector的對象
/// @param selector selector
/// @param delay 延遲執(zhí)行的秒數(shù)
/// @param interval 間隔時間
/// @param repeats 是否重復
/// @param async 在主線程還是子線程執(zhí)行
+ (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