前言
在使用NSTimer荸镊,如果使用不得當(dāng)特別會(huì)引起循環(huán)引用铃慷,造成內(nèi)存泄露。所以怎么避免循環(huán)引用問題剧罩,下面我提出幾種解決NSTimer的幾種循環(huán)引用。
原因
當(dāng)你在ViewController(簡稱VC)中使用timer屬性座泳,由于VC強(qiáng)引用timer,timer的target又是VC造成循環(huán)引用幕与。當(dāng)你在VC的dealloc方法中銷毀timer挑势,
發(fā)現(xiàn)VC被pop,VC的dealloc方法沒走啦鸣,VC在等timer釋放才走dealloc潮饱,timer釋放在dealloc中,所以引起循環(huán)引用诫给。
解決方案
方案一
//1香拉、類別中使用copy block的形式使timer只引用自己而不引用TimerViewController(可以釋放)
//如果在block里面直接調(diào)用self啦扬,還是會(huì)保留環(huán)的。因?yàn)閎lock對self強(qiáng)引用凫碌,self對timer強(qiáng)引用扑毡,timer又通過userInfo參數(shù)保留block(強(qiáng)引用block),這樣就構(gòu)成一個(gè)環(huán)block->self->timer->userinfo->block,所以要打破這個(gè)環(huán)的話要在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)
/*
該方案主要要點(diǎn):
將計(jì)時(shí)器所應(yīng)執(zhí)行的任務(wù)封裝成"Block"瞄摊,在調(diào)用計(jì)時(shí)器函數(shù)時(shí),把block作為userInfo參數(shù)傳進(jìn)去苦掘。
userInfo參數(shù)用來存放"不透明值"换帜,只要計(jì)時(shí)器有效,就會(huì)一直保留它鹤啡。
在傳入?yún)?shù)時(shí)要通過copy方法惯驼,將block拷貝到"堆區(qū)",否則等到稍后要執(zhí)行它的時(shí)候递瑰,該blcok可能已經(jīng)無效了祟牲。
計(jì)時(shí)器現(xiàn)在的target是NSTimer類對象,這是個(gè)單例泣矛,因此計(jì)時(shí)器是否會(huì)保留它疲眷,其實(shí)都無所謂。此處依然有保留環(huán)您朽,然而因?yàn)轭悓ο螅╟lass object)無需回收狂丝,所以不用擔(dān)心。
*/
-
(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哗总、使用系統(tǒng)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(""){
//消息轉(zhuǎn)發(fā)(返回值methodSignature不能為空(調(diào)用這個(gè)方法的類沒有實(shí)現(xiàn)這個(gè)方法就會(huì)為空))
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(當(dāng)NSTimer 調(diào)用invalidate的時(shí)候釋放weakTimer)
//NSTimer 的target 都是強(qiáng)引用(即使使用__weak __typeof(&self)weakself = self 對weakself也是強(qiáng)引用讯屈,weakself也會(huì)和NSTimer引起循環(huán)引用)
}
@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;
}
//通過類方法創(chuàng)建創(chuàng)建
- (instancetype)proxyWithObjc:(id)object{
return [[self alloc] initWithObjc:object];
}
(void)forwardInvocation:(NSInvocation *)anInvocation{
[anInvocation invokeWithTarget:self.target];
}(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
//消息轉(zhuǎn)發(fā)(返回值methodSignature不能為空(調(diào)用這個(gè)方法的類沒有實(shí)現(xiàn)這個(gè)方法就會(huì)為空))
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(當(dāng)NSTimer 調(diào)用invalidate的時(shí)候釋放weakTimer)
//NSTimer 的target 都是強(qiáng)引用(即使使用__weak __typeof(&self)weakself = self 對weakself也是強(qiáng)引用蛋哭,weakself也會(huì)和NSTimer引起循環(huán)引用)
}
@end
方案四
//4、在當(dāng)前類釋放前釋放NSTimer(在ViewController執(zhí)行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;
}
}
作者:學(xué)不來的凡人
鏈接:http://www.reibang.com/p/fb52c60416ac
來源:簡書
著作權(quán)歸作者所有涮母。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán)谆趾,非商業(yè)轉(zhuǎn)載請注明出處。