iOS NSTimer 解决循环引用

160 阅读1分钟

1. 中间对象解决循环引用

可以使用一个中间对象,对 NSTimer 和 target进行弱引用,这样就解决了。当 VC 销毁了,target 也会销毁,可以通过中间对象来判断 target 是否为 nil。这样的话就可以把 NSTimer 置为无效和 nil,这样也就打破了循环引用的目的了。

image.png

#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;
    }
}