NSTimer 使用中需要注意的有两点: 防止循环引用
和 添加到对应的runloop
, 本次主要关注循环应用的解决方案.
/**
使用 NSTimer - block方法, 可以通过使用 __weak 打破循环引用, 比较推荐
*/
__weak typeof(self) weakSelf = self;
self.timer = [NSTimer scheduledTimerWithTimeInterval:2.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
__strong typeof(weakSelf) strongSelf = weakSelf;
NSLog(@"%s ---", __func__);
CGFloat random = arc4random() % 255;
strongSelf.view.backgroundColor = [UIColor colorWithRed:random / 255.0 green:random / 255.0 blue:random / 255.0 alpha:1];
}];
/**
使用 NSTimer - target方法, 可以通过 NSProxy 方法交换来打破循环应用, 需要在 -dealloc 中将timer销毁(invalidate), 也可以在其他合适的时机将timer销毁, 否则报错
"*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSProxy doesNotRecognizeSelector:__timerAction] called!'"
*/
self.timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:[OPProxy proxyWithTarget:self] selector:@selector(__timerAction) userInfo:nil repeats:YES];
dispath_source_t timer, 我比较推荐使用这种方式. 这种方式是通过内核函数获取时间, 不会受runloop轮询的影响, 时间更加准确. 使用底层函数, 效率更高.
/**
使用 NSTimer - block方法, 可以通过使用 __weak 打破循环引用, 比较推荐
*/
__weak typeof(self) weakSelf = self;
self.timer = [NSTimer scheduledTimerWithTimeInterval:2.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
__strong typeof(weakSelf) strongSelf = weakSelf;
NSLog(@"%s ---", __func__);
CGFloat random = arc4random() % 255;
strongSelf.view.backgroundColor = [UIColor colorWithRed:random / 255.0 green:random / 255.0 blue:random / 255.0 alpha:1];
}];
/**
使用 NSTimer - target方法, 可以通过 NSProxy 方法交换来打破循环应用, 需要在 -dealloc 中将timer销毁(invalidate), 也可以在其他合适的时机将timer销毁, 否则报错
"*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSProxy doesNotRecognizeSelector:__timerAction] called!'"
*/
self.timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:[OPProxy proxyWithTarget:self] selector:@selector(__timerAction) userInfo:nil repeats:YES];
以下是个人封装的工具方法仅供参考:
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface OPProxy : NSProxy
@property (weak, nonatomic, readonly) id target;
+ (instancetype)proxyWithTarget:(id)target;
@end
NS_ASSUME_NONNULL_END
#import "OPProxy.h"
@interface OPProxy ()
@property (weak, nonatomic, readwrite) id target;
@end
@implementation OPProxy
+ (instancetype)proxyWithTarget:(id)target {
OPProxy *proxy = [OPProxy alloc];
proxy.target = target;
return proxy;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
return [self.target methodSignatureForSelector:sel];
}
- (void)forwardInvocation:(NSInvocation *)invocation {
[invocation invokeWithTarget:self.target];
}
@end
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface OPTimer : NSObject
+ (NSString *)executTask:(void(^)(void))task
start:(NSTimeInterval)start
timeInterval:(NSTimeInterval)timeInterval
repeat:(BOOL)repeat
async:(BOOL)async;
+ (NSString *)executTask:(id)target
selector:(SEL)selector
start:(NSTimeInterval)start
timeInterval:(NSTimeInterval)timeInterval
repeat:(BOOL)repeat
async:(BOOL)async;
+ (void)cancelTask:(NSString *)taskId;
@end
NS_ASSUME_NONNULL_END
#import "OPTimer.h"
#import "OPProxy.h"
static NSMutableDictionary *__timersDictionary;
dispatch_semaphore_t __timerSemaphore;
@implementation OPTimer
+ (void)initialize {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
__timersDictionary = [NSMutableDictionary dictionary];
__timerSemaphore = dispatch_semaphore_create(1);
});
}
+ (NSString *)executTask:(void (^)(void))task
start:(NSTimeInterval)start
timeInterval:(NSTimeInterval)timeInterval
repeat:(BOOL)repeat
async:(BOOL)async {
if (!task || start < 0 || (timeInterval <= 0 && repeat)) {
return nil;
}
dispatch_queue_t queue = async ? dispatch_get_global_queue(QOS_CLASS_DEFAULT, 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_NOW, timeInterval * NSEC_PER_SEC, start * NSEC_PER_SEC);
dispatch_semaphore_wait(__timerSemaphore, DISPATCH_TIME_FOREVER);
NSString *timerKey = [[NSString alloc] initWithFormat:@"%zd", __timersDictionary.count];
__timersDictionary[timerKey] = timer;
dispatch_semaphore_signal(__timerSemaphore);
dispatch_source_set_event_handler(timer, ^{
task();
if (!repeat) {
[self cancelTask:timerKey];
}
});
dispatch_resume(timer);
return timerKey;
}
+ (NSString *)executTask:(id)target
selector:(SEL)selector
start:(NSTimeInterval)start
timeInterval:(NSTimeInterval)timeInterval
repeat:(BOOL)repeat
async:(BOOL)async {
if (!target || !selector) {
return nil;
}
return [self executTask:^{
if ([[OPProxy proxyWithTarget:target] respondsToSelector:selector]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[[OPProxy proxyWithTarget:target] performSelector:selector];
#pragma clang diagnostic pop
}
} start:start timeInterval:timeInterval repeat:repeat async:async];
}
+ (void)cancelTask:(NSString *)taskId {
if (![taskId length]) {
return;
}
dispatch_semaphore_wait(__timerSemaphore, DISPATCH_TIME_FOREVER);
dispatch_source_t timer = __timersDictionary[taskId];
if (timer) {
dispatch_source_cancel(timer);
[__timersDictionary removeObjectForKey:taskId];
}
dispatch_semaphore_signal(__timerSemaphore);
}
@end
网友评论