1. 中间对象解决循环引用
可以使用一个中间对象,对 NSTimer 和 target进行弱引用,这样就解决了。当 VC 销毁了,target 也会销毁,可以通过中间对象来判断 target 是否为 nil。这样的话就可以把 NSTimer 置为无效和 nil,这样也就打破了循环引用的目的了。
#import "NSTimer+weakTimer.h"
@interface TimerWeakObject : NSObject
/** target */
@property (nonatomic, weak) id target;
/** timer到期的回调方法selector */
@property (nonatomic, assign) SEL selector;
/** timer */
@property (nonatomic, weak) NSTimer *timer;
- (void)cancel:(NSTimer*)timer;
@end
@implementation TimerWeakObject
- (void)cancel:(NSTimer*)timer {
//判断 target是否存在
if (self.target) {
if ([self.target respondsToSelector:self.selector]) {
[self.target performSelector:self.selector withObject:timer.userInfo];
}
}else{
//不存在就把定时器置为无效,也就是runLoop 对timer强引用的释放
//同时timer也会对TimerWeakObject释放
[self.timer invalidate];
}
}
@end
@implementation NSTimer (weakTimer)
+ (NSTimer*)scheduledWeakTimerWithTimeInterval:(NSTimeInterval)interval target:(id)target selector:(SEL)selector userInfo:(id)userInfo repeats:(BOOL)repeats {
TimerWeakObject *obj = [[TimerWeakObject alloc]init];
obj.target = target;
obj.selector = selector;
// 创建系统的NSTimer给TimerWeakObject
obj.timer = [NSTimer scheduledTimerWithTimeInterval:interval target:obj selector:@selector(cancel:) userInfo:userInfo repeats:repeats];
return obj.timer;
}
@end
这里是通过创建一个NSTimer的分类,分类里面创建了一个中间对象,通过中间对象来处理外部的NSTimer事件。
使用起来也是很简单,一行代码搞定。
self.timer = [NSTimer scheduledWeakTimerWithTimeInterval:1 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
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里面进行相关取消定时器的操作就就可以了。
3. 导航控制器push方法(不适应present)
//生命周期 移除VC的时候,这种适合 push的页面,不适应Present
- (void)didMoveToParentViewController:(UIViewController *)parent {
if (parent == nil) {
[self.timer invalidate];
self.timer = nil;
}
}