1. NSTimer的使用
常见使用场景如下:
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
}
- (void)timerAction{
NSLog(@"打印了");
}
- (void)dealloc {
[self.timer invalidate];
self.timer = nil;
NSLog(@"** dealloc **");
}
但是在页面销毁的时候,并没有走dealloc方法,定时器还在运行,这就造成内存泄漏了。
原因:由于NSTimer
会引用住self
,而 self
又持有NSTimer
对象,所以形成循环引用,dealloc
永远不会被执行,timer
也永远不会被释放,造成内存泄漏
常见解决方法:
1.中间对象解决循环引用
中间对象代码如下:
@interface TimerWeakObject : NSObject
+ (instancetype)proxyWithTarget:(id)target;
@end
@interface TimerWeakObject ()
@property (nonatomic ,weak)id target;
@end
@implementation TimerWeakObject
+ (instancetype)proxyWithTarget:(id)target {
//NSProxy实例方法为alloc
TimerWeakObject *proxy = [TimerWeakObject alloc];
proxy.target = target;
return proxy;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
return [self.target methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation{
[anInvocation invokeWithTarget:self.target];
}
@end
界面调用代码如下:
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:[TimerWeakObject proxyWithTarget:self] selector:@selector(timerAction) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
}
- (void)timerAction{
NSLog(@"打印了");
}
- (void)dealloc {
[self.timer invalidate];
self.timer = nil;
NSLog(@"** dealloc **");
}
效果非常的明显,dealloc
也走了,说明对象释放了,没有强引用了。
2.系统api方法解决循环引用
在iOS 10以后系统,苹果针对NSTimer
进行了优化,使用Block回调方式,解决了循环引用问题。
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
NSLog(@"打印了");
}];
- (void)dealloc {
[self.timer invalidate];
self.timer = nil;
NSLog(@"** dealloc **");
}
使用这种系统的 api方法,会执行的dealloc
,只要在dealloc
里面进行相关取消定时器的操作就就可以了。