目录
-
NSTimer的基础用法
-
NSTimer的内存泄露
-
安全防侧漏的定时器
NSTimer的基础用法
创建定时器
// 初始化定时器后直接加入到当前运行循环RunLoop中 在TimeInterval后 自动启动
+(NSTimer *)scheduledTimerWithTimeInterval:ti invocation:invocation repeats:yesOrNo;
+(NSTimer *)scheduledTimerWithTimeInterval:ti target:(id)aTarget selector:aSelector userInfo:userInfo repeats:yesOrNo;
// 创建但不会加入到runloop中 建立之后 须手动加入
+(NSTimer *)timerWithTimeInterval:ti invocation:invocation repeats:yesOrNo;
+(NSTimer *)timerWithTimeInterval:ti target:aTarget selector:aSelector userInfo:userInfo repeats:yesOrNo;
// 需手动加入RunLoop
self.timer =[NSTimer timerWithTimeInterval:5.0 target:self selector:@selector(scanError) userInfo:nil repeats:YES];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addTimer:self.timer forMode:NSDefaultRunLoopMode];
运行和停止定时器
// 是否在运行
@property (readonly, getter=isValid) BOOL valid;
//停止
[timer setFireDate:[NSDate distantFuture]];
//运行
[timer setFireDate:[NSDate distantPast]];
移除定时器
-(void)clearTimer{
if(self.timer != nil){
[self.timer invalidate];
self.timer = nil;
}
}
NSTimer的内存泄露
引起内存泄露的原因:
NSTimer要结合NSRunloop来使用, 当timer加入runloop时, 实际上是runloop强引用了timer. 如果我们现在有一个控制器vc(或者视图) 使用了timer, 将timer作为其成员变量, 这个vc强引用了这个timer, runloop一直存在, 所以timer不会释放, 所以这个vc也不会释放. 这样就出现了内存泄露.
实际上, 当你吧vc的timer设置为弱引用(weak), 也是无济于事的. 当你吧timer的target
设置为self (指这个vc)时, timer对这个vc依然是强引用.
/*
** 定时器内存泄露原因
-------------- {一直存在的 runloop} -------------------
|||
-------------- {timer 加入了runloop} ------------------
|||
-------------- {timer作为vc的属性被vc强引用} ------------
-------------- {vc作为timer的target被timer强引用} ------
*/
安全防侧漏的定时器
方法一
当这个控制器在退出时(退出按钮, 点击时执行dismiss或者pop) 这退出前执行 [self.timer invalidate];
可以释放这个timer, 解决内存泄露问题. 由此我们也可以推测, timer的 invalidate方法应该释放了强引用的target, 退出了runloop.
方法二
写一个安全的定时器方法, 承接timer对self的强引用.
//
// WSafeTimer.h
//
#import <Foundation/Foundation.h>
@interface WSafeTimer : NSObject
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;
@end
//
// WSafeTimer.m
//
#import "WSafeTimer.h"
@interface WSafeTimer()
@property(nonatomic,weak) NSTimer *timer;
@property(nonatomic,weak) id target;
@property(nonatomic,assign) SEL selector;
@end
@implementation WSafeTimer
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo{
WSafeTimer *safeTimer = [WSafeTimer new];
safeTimer.target = aTarget;
safeTimer.selector = aSelector;
safeTimer.timer = [NSTimer scheduledTimerWithTimeInterval:ti target:safeTimer selector:@selector(timerHandle) userInfo:userInfo repeats:yesOrNo];
return safeTimer.timer;
}
-(void)timerHandle{
if (self.target && [self.target respondsToSelector:self.selector]) {
[self.target performSelector:self.selector];
}else {
[self.timer invalidate];
}
}
@end
//
// 使用方法与正常的定时器无差异
//
#import "MySafeView.h"
#import "WSafeTimer.h"
@interface MySafeView()
@property(nonatomic,strong) NSTimer *timer;
@end
@implementation MySafeView
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// 创建定时器, 使用WSafeTimer创建
self.timer = [WSafeTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(changeTimer) userInfo:nil repeats:YES];
[self.timer setFireDate:[NSDate distantPast]];
}
return self;
}
-(void)changeTimer{
NSLog(@"changeTimer run");
}
-(void)clearTimer{
if(self.timer != nil){
[self.timer invalidate];
self.timer = nil;
}
}
-(void)dealloc{
NSLog(@"dealloc run");
[self clearTimer];
}
@end
网友评论