前言
在使用NSTimer,如果使用不得当特别会引起循环引用,造成内存泄露。所以怎么避免循环引用问题,下面我提出几种解决NSTimer的几种循环引用。
原因
当你在ViewController(简称VC)中使用timer属性,由于VC强引用timer,timer的target又是VC造成循环引用。当你在VC的dealloc方法中销毁timer,
发现VC被pop,VC的dealloc方法没走,VC在等timer释放才走dealloc,timer释放在dealloc中,所以引起循环引用。
解决方案
方案一
//1、类别中使用copy block的形式使timer只引用自己而不引用TimerViewController(可以释放)
//如果在block里面直接调用self,还是会保留环的。因为block对self强引用,self对timer强引用,timer又通过userInfo参数保留block(强引用block),这样就构成一个环block->self->timer->userinfo->block,所以要打破这个环的话要在block里面弱引用self。
- (NSTimer )timerWithLFJBlockSupport{
__weak __typeof(&self)weakSelf = self;
NSTimer *timer = [NSTimer lfj_scheduledTimerWithTimeInterval:1 repeats:YES block:^{
[weakSelf countAddWith:28];
}];
return timer;
}
import "NSTimer+LFJBlockSupport.h"
@implementation NSTimer (LFJBlockSupport)
/*
该方案主要要点:
将计时器所应执行的任务封装成"Block",在调用计时器函数时,把block作为userInfo参数传进去。
userInfo参数用来存放"不透明值",只要计时器有效,就会一直保留它。
在传入参数时要通过copy方法,将block拷贝到"堆区",否则等到稍后要执行它的时候,该blcok可能已经无效了。
计时器现在的target是NSTimer类对象,这是个单例,因此计时器是否会保留它,其实都无所谓。此处依然有保留环,然而因为类对象(class object)无需回收,所以不用担心。
*/
-
(NSTimer*)lfj_scheduledTimerWithTimeInterval:(NSTimeInterval)timeInterval repeats:(BOOL)repeats block:(void(^)(void))block{
return [self scheduledTimerWithTimeInterval:timeInterval target:self selector:@selector(lfj_blockSelector:) userInfo:[block copy] repeats:repeats];
}
+(void)lfj_blockSelector:(NSTimer *)timer{
void(^block)(void) = timer.userInfo;
if (block) {
block();
}
}
@end
方案二
//2、使用系统block(不过只支持iOS 10以后)(可以释放)
- (NSTimer )timerWithsystemBlock{
__weak __typeof(&(self))weakSelf = self;
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
[weakSelf countAddWith:28];
}];
return timer;
}
方案三
//3、使用中间类弱引用(weak)target
- (NSTimer *)timerWithWeakTimer{
NSTimer *timer = [LFJWeakTimer weak_lfjScheduledTimerWithTimeInterval:1 target:self selector:@selector(countAddWith:) userInfo:nil repeats:YES];
return timer;
}
//3、使用中间类弱引用(weak)target - (NSTimer *)timerWithProxyTimerTimer{
NSTimer *timer = [LFJProxyTimer weak_lfjScheduledTimerWithTimeInterval:1 target:self selector:@selector(countAddWith:) userInfo:nil repeats:YES];
return timer;
}
import "LFJWeakTimer.h"
@interface LFJWeakTimer()
@property (nonatomic,weak)id target;
@end
@implementation LFJWeakTimer
(void)forwardInvocation:(NSInvocation *)anInvocation OBJC_SWIFT_UNAVAILABLE(""){
[anInvocation invokeWithTarget:self.target];
}(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector OBJC_SWIFT_UNAVAILABLE(""){
//消息转发(返回值methodSignature不能为空(调用这个方法的类没有实现这个方法就会为空))
return [self.target methodSignatureForSelector:aSelector];
}(void)dealloc{
NSLog(@"weakTimer------释放");
}
- (NSTimer *)weak_lfjScheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo{
LFJWeakTimer weakTimer = [LFJWeakTimer new];
weakTimer.target = aTarget;
return [NSTimer scheduledTimerWithTimeInterval:ti target:weakTimer selector:aSelector userInfo:userInfo repeats:yesOrNo];
//NSTimer 持有---->weakTimer 弱引用--->aTarget(当NSTimer 调用invalidate的时候释放weakTimer)
//NSTimer 的target 都是强引用(即使使用__weak __typeof(&self)weakself = self 对weakself也是强引用,weakself也会和NSTimer引起循环引用)
}
@end
@interface LFJProxyTimer : NSProxy
- (instancetype)proxyWithObjc:(id)object;
- (NSTimer *)weak_lfjScheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;
@end
import "LFJProxyTimer.h"
@interface LFJProxyTimer()
@property (nonatomic,weak)id target;
@end
@implementation LFJProxyTimer
-
(instancetype)initWithObjc:(id)object {
self.target = object;
return self;
}
//通过类方法创建创建
- (instancetype)proxyWithObjc:(id)object{
return [[self alloc] initWithObjc:object];
}
(void)forwardInvocation:(NSInvocation *)anInvocation{
[anInvocation invokeWithTarget:self.target];
}(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
//消息转发(返回值methodSignature不能为空(调用这个方法的类没有实现这个方法就会为空))
return [self.target methodSignatureForSelector:aSelector];
}(void)dealloc{
NSLog(@"weakTimer------释放");
}
- (NSTimer *)weak_lfjScheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo{
LFJProxyTimer proxyTimer = [LFJProxyTimer proxyWithObjc:aTarget];
proxyTimer.target = aTarget;
return [NSTimer scheduledTimerWithTimeInterval:ti target:proxyTimer selector:aSelector userInfo:userInfo repeats:yesOrNo];
//NSTimer 持有---->weakTimer 弱引用--->aTarget(当NSTimer 调用invalidate的时候释放weakTimer)
//NSTimer 的target 都是强引用(即使使用__weak __typeof(&self)weakself = self 对weakself也是强引用,weakself也会和NSTimer引起循环引用)
}
@end
方案四
//4、在当前类释放前释放NSTimer(在ViewController执行dealloc前释放timer(不推荐))
(NSTimer *)timerWithNormal{
objc_setAssociatedObject(self, &nameKey, @"1", OBJC_ASSOCIATION_ASSIGN);
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(countAddWith:) userInfo:@34 repeats:YES];
return timer;
}(void)viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:animated];
NSString *dd = objc_getAssociatedObject(self, &nameKey);
if (dd) {
[self releaseTimer];
}
}(void)dealloc{
NSLog(@"释放");
[self releaseTimer];
}(void)restartTimer {
[self startTimer];
}(void)releaseTimer{
if (self.timer) {
[self.timer invalidate];
self.timer = nil;
}
}
作者:学不来的凡人
链接:https://www.jianshu.com/p/fb52c60416ac
来源:
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。