NSTimer:
一个在确定时间间隔内执行一次或多次我们指定对象方法的对象。
基本使用:
两个比较常用的方法:
timerWithTimeInterval: target: selector: userInfo: repeats:;
scheduledTimerWithTimeInterval: target: selector: userInfo: repeats:;
区别:
第一种方式的使用:
_timer = [NSTimer timerWithTimeInterval:1.f target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
//默认是 NSDefaultRunLoopMode
//NSRunLoopCommonModes 包含了 NSDefaultRunLoopMode 和 UITrackingRunLoopMode,所以滑动的时候也能响应定时器
[[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
第二种方式:
//默认会自动添加到 NSDefaultRunLoopMode
_timer = [NSTimer scheduledTimerWithTimeInterval:1.f target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
- (void)timerAction {
NSLog(@" === timerAction == ");
}
[[NSRunLoop currentRunLoop] run];
[self performSelector:@selector(invalidateTimer) withObject:nil afterDelay:3];
//子线程中使用定时器
[NSThread detachNewThreadSelector:@selector(threadTimer) toTarget:self withObject:nil];
//NSTimer 在线程中的使用
- (void)threadTimer {
NSLog(@"%@",[NSThread currentThread]);
//直接调用是没有任何反应的
//runloop 在子线程中是需要手动开启的
_timer = [NSTimer timerWithTimeInterval:1.f target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSDefaultRunLoopMode];
//子线程上的定时器,必须要在子线程中销毁,不要在主线程中销毁,否者会造成runloop 资源泄露
[self performSelector:@selector(invalidateTimer) withObject:nil afterDelay:3];
//手动开启runoop
[[NSRunLoop currentRunLoop] run];
//runloop 的创建方式不是通过alloc init 是通过 [NSRunLoop currentRunLoop] 来直接获取的
NSLog(@" === 子线程的timer 销毁了 === ");
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//如果在当前线程有复杂的操作,会导致定时器卡住
[self busyCalculate];
});
//如果在当前线程有复杂的操作,会导致定时器卡住
- (void)busyCalculate {
NSUInteger count = 0xFFFFFFF;
CGFloat num = 0;
for (int i = 0; i < count; i ++) {
num = i/count;
}
}
Description:
//invalidate方法 会停止计时器的再次触发,并在RunLoop中将其移除。
Stops the timer from ever firing again and requests its removal from its run loop.
//invalidate 方法 是将NSTimer对象从RunLoop 中移除的唯一方法。
This method is the only way to remove a timer from an NSRunLoop object.
//调用invalidate方法会删除RunLoop对NSTimer的强引用,以及NSTimer对target和userInfo的强引用!
The NSRunLoop object removes its strong reference to the timer, either just before the invalidate method returns or at some later point.
If it was configured with target and user info objects, the receiver removes its strong references to those objects as well.
//计时器与运行循环一起工作。RunLoop 维持着对定时器的强引用。
Timers work in conjunction with run loops. Run loops maintain strong references to their timers, so you don’t have to maintain your own strong reference to a timer after you have added it to a run loop.
//当计时器触发后,在调用invalidated 之前会一直保持对target的强引用
The object to which to send the message specified by aSelector when the timer fires. The timer maintains a strong reference to this object until it (the timer) is invalidated.
@interface TimerViewController ()
@property (nonatomic,strong) NSTimer *timer;
@end
@implementation TimerViewController
- (void)viewDidLoad {
[super viewDidLoad];
_timer = [NSTimer timerWithTimeInterval:1.f target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSDefaultRunLoopMode];
//_timer = [NSTimer scheduledTimerWithTimeInterval:1.f target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
}
- (void)timerAction {
NSLog(@" === timerAction == ");
}
- (void)dealloc {
[_timer invalidate];
_timer = nil;
NSLog(@" _timer invalidate 销毁了");
}
@end
以上代码平时我们一般都是这样使用的,但是这样dealloc 方法一般是不会走到的,这样定时器是永远都不会销毁的。
解决循环引用的方法有三种
- 一般情况下在直接在 viewWillDisappear 中手动去销毁定时器
- 自己实现一个带block的定时器分类,实现一个不保留目标对象的定时器
- 通过NSProxy 来处理内存泄露的问题
1、在viewWillDisappear 中手动去销毁定时器
//可以手动在 viewWillDisappear 中去销毁定时器
//在控制器即将销毁的时候销毁定时器,这样定时器对控制的强引用就解除了,循环引用也解除了
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[_timer invalidate];
_timer = nil;
}
@interface NSTimer (RCTimer)
+ (instancetype)timerWithTimeInterval:(NSTimeInterval)ti block:(void(^)(void))block repeats:(BOOL)repeats;
@end
@implementation NSTimer (RCTimer)
+ (instancetype)timerWithTimeInterval:(NSTimeInterval)ti block:(void (^)(void))block repeats:(BOOL)repeats {
return [NSTimer timerWithTimeInterval:ti target:self selector:@selector(timerAction:) userInfo:block repeats:repeats];
}
+ (void)timerAction:(NSTimer *)timer {
void(^block)(void) = [timer userInfo];
if (block) {
block();
}
}
@end
- (void)viewDidLoad {
[super viewDidLoad];
//引起循环引用的主要是 targrt:self
//自己实现一个不保留目标的定时器
__weak typeof(self)Weakself = self;
_timer = [NSTimer timerWithTimeInterval:1.f block:^{
[Weakself timerAction];
} repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
}
- (void)timerAction {
NSLog(@" === timerAction == ");
}
- (void)dealloc {
// 务必在当前线程调用invalidate方法,使得Runloop释放对timer的强引用
[_timer invalidate];
_timer = nil;
}
//在iOS 10 之后系统提供了类似的block 的方法来解决循环引用的问题
__weak typeof(self)Weakself = self;
_timer = [NSTimer timerWithTimeInterval:1.f repeats:YES block:^(NSTimer * _Nonnull timer) {
[Weakself timerAction];
}];
[[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
//schedule方式
_timer = [NSTimer scheduledTimerWithTimeInterval:1.f repeats:YES block:^(NSTimer * _Nonnull timer) {
[Weakself timerAction];
}];
#import
@interface RCProxy : NSProxy
@property (nonatomic, weak, readonly) id target;
-(instancetype)initWithTarget:(id)target;
+(instancetype)proxyWithTarget:(id)target;
@end
@implementation RCProxy
-(instancetype)initWithTarget:(id)target {
_target = target;
return self;
}
+(instancetype)proxyWithTarget:(id)target {
return [[RCProxy alloc] initWithTarget:target];
}
-(id)forwardingTargetForSelector:(SEL)selector {
return _target;
}
-(void)forwardInvocation:(NSInvocation *)invocation {
void *null = NULL;
[invocation setReturnValue:&null];
}
-(NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
return [NSObject instanceMethodSignatureForSelector:@selector(init)];
}
-(BOOL)respondsToSelector:(SEL)aSelector {
return [_target respondsToSelector:aSelector];
}
-(BOOL)isEqual:(id)object {
return [_target isEqual:object];
}
-(NSUInteger)hash {
return [_target hash];
}
-(Class)superclass {
return [_target superclass];
}
-(Class)class {
return [_target class];
}
-(BOOL)isKindOfClass:(Class)aClass {
return [_target isKindOfClass:aClass];
}
-(BOOL)isMemberOfClass:(Class)aClass {
return [_target isMemberOfClass:aClass];
}
-(BOOL)conformsToProtocol:(Protocol *)aProtocol {
return [_target conformsToProtocol:aProtocol];
}
-(BOOL)isProxy {
return YES;
}
-(NSString *)description {
return [_target description];
}
-(NSString *)debugDescription {
return [_target debugDescription];
}
@end
- (void)viewDidLoad {
[super viewDidLoad];
_timer = [NSTimer scheduledTimerWithTimeInterval:1
target:[RCProxy proxyWithTarget:self]
selector:@selector(timerAction)
userInfo:nil
repeats:YES];
}
}
- (void)timerAction {
NSLog(@" === timerAction == ");
}
- (void)dealloc {
[_timer invalidate];
_timer = nil;
NSLog(@" _timer invalidate 销毁了");
}
@property (nonatomic,strong) dispatch_source_t timer;
- (void)gcdTimer:(NSTimeInterval)timeInterval repeats:(BOOL)repeats {
//获取队列
dispatch_queue_t queue = dispatch_queue_create(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//创建一个定时器(dispatch_source_t本质还是个OC对象,创建出来的对象需要强引用)
_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
//设置定时器的各种属性(什么时候开始任务,每隔多久执行一次) GCD的时间参数,一般是纳秒(1秒 = 10的9次方纳秒)
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, timeInterval * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
//设置回调
dispatch_source_set_event_handler(_timer, ^{
if (!repeats) {
dispatch_cancel(_timer); //取消定时器
_timer = nil;
}else {
[self timerAction];
}
});
//启动定时器
dispatch_resume(_timer);
//只执行一次操作
// dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//
// });
}