NSTimer 的循环引用,出现在 timer 作为ViewController的属性,同时timer中的 target为self
graph TD
ViewController --> timer --> ViewController
破除循环引用,有三种方式。
1、在viewWillDisappear中结束定时器
- (void)viewWillDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
if (_timer) {
[_timer invalidate];
_timer = nil;
}
}
这样可以运行到viewcontroller的dealloc,问题是,页面push后再pop,没有定时器工作,所以,可以考虑在viewWillAppear中开启定时器。
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if (_timer) {
return;
}
_timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
}
2、封装NSTimer
将timer的内容,放到封装类中,同时在viewcontroller中的dealloc中,释放封装类中的timer
封装类
@interface TestTimer1 : NSObject
- (void)startTimer;
- (void)endTimer;
@end
@interface TestTimer1 ()
@property (nonatomic, strong) NSTimer *timer;
@end
@implementation TestTimer1
- (void)startTimer {
_timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(onTimer) userInfo:nil repeats:YES];
}
- (void)onTimer {
NSLog(@"timerAction");
}
- (void)endTimer {
if (_timer) {
[_timer invalidate];
_timer = nil;
}
}
- (void)dealloc {
NSLog(@"TestTimer1 dealloc");
}
@end
viewcontroller中
- (void)viewDidLoad {
[super viewDidLoad];
_timer1 = [TestTimer1 new];
[_timer1 startTimer];
}
- (void)dealloc {
[_timer1 endTimer];
NSLog(@"ViewController2 dealloc");
}
这样,viewcontroller只有强引用TestTimer1。
3、使用NSProxy
NSProxy是一个抽象类,一般用来负责代理消息转发的对象。必须通过子类实例化。
@interface TestTimer2 : NSProxy
- (instancetype)initWithObject:(id)obj;
@end
@interface TestTimer2 ()
@property (nonatomic, weak) id obj;
@end
@implementation TestTimer2
- (instancetype)initWithObject:(id)obj {
self.obj = obj;
return self;
}
// 消息转发
- (void)forwardInvocation:(NSInvocation *)invocation {
if ([self.obj respondsToSelector:invocation.selector]) {
[invocation invokeWithTarget: self.obj];
}
}
// 签名注册方法
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
return [self.obj methodSignatureForSelector:sel];
}
@end
在viewcontroller中,记得要在dealloc中释放定时器,否则会出现 "[NSProxy doesNotRecognizeSelector:timerAction]"的崩溃错误。
- (void)viewDidLoad {
[super viewDidLoad];
TestTimer2 *timer2 = [[TestTimer2 alloc] initWithObject:self];
_timer = [NSTimer scheduledTimerWithTimeInterval:1 target:timer2 selector:@selector(timerAction) userInfo:nil repeats:YES];
}
- (void)timerAction {
NSLog(@"timerAction");
}
- (void)dealloc {
if (_timer) {
[_timer invalidate];
_timer = nil;
}
NSLog(@"ViewController2 dealloc");
}
viewcontroller 弱引用 timer2, _timer强引用timer2,timer2弱引用 viewcontroller。