在arc循環(huán)引用的案例中,其中有一種是timer導(dǎo)致的。
主要原因歸結(jié)為
舉例來說明
//TimerController.h
#import@interface TimerController : UIViewController
@property(nonatomic,strong) NSTimer* timer;
@end
//TimerController.m
-(void)viewDidLoad
{
? ? ?[super viewDidLoad];
? ? ?self.view.backgroundColor = [UIColor whiteColor];
? ? ?NSTimer* tmpTimer = [NSTimer scheduledTimerWithTimeInterval:1
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?target:self
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?selector:@selector(p_doSomeThing)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?userInfo:nil
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?repeats:YES];
}
如果repeats = NO 是不會產(chǎn)生循環(huán)引用問題的。 因?yàn)樽詣俞尫懦貢尫舤mpTimer中所分配的內(nèi)存,這樣保留環(huán)也就不存在了。
那么如果repeats = YES, 那么就會產(chǎn)生tmpTimer保留self,并且一直會被保留,導(dǎo)致控制器的dealloc方法沒法被調(diào)用,導(dǎo)致內(nèi)存泄漏。并且如果不執(zhí)行invalidate定時器仍然繼續(xù)執(zhí)行。所以我覺得這里叫循環(huán)引用不是很貼切,因?yàn)閠mpTimer所指向的那段內(nèi)存沒法被釋放。
那么tmpTimer內(nèi)存何時能被釋放呢? timer調(diào)用invalidate的時候,為了能在適當(dāng)?shù)牡胤娇梢哉{(diào)用invalidate,那么我們用一個屬性保存tmpTimer,即self.timer = tmpTimer;
這樣我們明顯看出幾個問題,timer強(qiáng)引用了tmpTimer這塊內(nèi)存,tmpTimer又強(qiáng)引用了self,self又強(qiáng)引用了timer, 明顯的循環(huán)引用產(chǎn)生了。 那么是不是將timer設(shè)成weak就解決問題了呢? 顯然不是的,原因如我上面所說,timer引起內(nèi)存泄漏可以概括為兩個原因
1. timer對target的強(qiáng)引用
2.由于是repeats,timer所指向的那塊內(nèi)存是沒辦法讓releasepool釋放的。
所以僅僅用循環(huán)引用來描述是不恰當(dāng)?shù)摹?/p>
invalidate的功效一方面是停止定時器的執(zhí)行,另一方面讓自動釋放池知道tmpTimer那段內(nèi)存可以進(jìn)行回收了。那么解決問題的一種思路 只要在適當(dāng)?shù)牡胤秸{(diào)用invalidate即萬事大吉了。控制器dealloc方法中肯定是不合適了,因?yàn)檫@個方法都沒機(jī)會給系統(tǒng)吊起。
-(void) viewWillDisappear:(BOOL)animated
{
? ? ?[super viewWillDisappear:animated];
? ? ?[_timer invalidate];
? ? ?_timer = nil;
}
這個方法中調(diào)用是可行的,并且通過調(diào)試發(fā)現(xiàn)內(nèi)存泄漏確實(shí)沒有了。如果controller出棧時候這么處理也蠻合理的,那么如果是由該控制器跳轉(zhuǎn)到另一控制器的情況呢,在這個地方調(diào)用就不合適了。
所以合理的邏輯應(yīng)該是要在該對象銷毀時才調(diào)用invalidate。
以上是我對timer引起內(nèi)存泄漏的一些自己的分析,那么解決方案如何? 網(wǎng)上有很多,這里拿來其中一種方案
在控制器對象中使用
_timer = [TimerWeakTarget scheduledTimerWithTimeInterval:1 target:self selector:@selector(p_doSomeThing) userInfo:nil repeats:YES];即可