1.Delegate/NSNotification
在使用代理設(shè)計(jì)模式的時(shí)候荒澡,一定要注意將 delegate 變量聲明為 weak 類型,像這樣
如使用strong或別的類型修飾的話將會(huì)導(dǎo)致循環(huán)引用实苞,導(dǎo)致dealloc()不會(huì)被調(diào)用缭受。NSNotification沒(méi)有移除通知等都會(huì)觸發(fā)一些意想不到的后果分瘾。
2.Block
目前在項(xiàng)目中出現(xiàn)的內(nèi)存泄漏大部分是因?yàn)閎lock的問(wèn)題想鹰。
在 ARC 下紊婉,當(dāng) block 獲取到外部變量時(shí),由于編譯器無(wú)法預(yù)測(cè)獲取到的變量何時(shí)會(huì)被突然釋放辑舷,為了保證程序能夠正確運(yùn)行喻犁,讓 block 持有獲取到的變量,向系統(tǒng)聲明:我要用它,你們千萬(wàn)別把它回收了株汉!然而,也正因 block 持有了變量歌殃,容易導(dǎo)致變量和 block 的循環(huán)引用乔妈,造成內(nèi)存泄露
[_sortButton setButtonSpreadPreAction:^BOOL{
if (_resultItems.count == 0) {
[progressHUD showText:@"xxxx"];
return NO;
}
return YES;
}];
-
這個(gè)例子的問(wèn)題就在于在使用 block 的過(guò)程中形成了循環(huán)引用:self 持有 sortButton;sortButton 持有 block氓皱;block 持有 self路召。三者形成循環(huán)引用,內(nèi)存泄露波材。
- 說(shuō)明原理:self —> _sortButton —> block —> _resultItems (_resultItems屬于self 相當(dāng)于 block—>self)
GCD已經(jīng)一些系統(tǒng)級(jí)的API并不會(huì)提示循環(huán)引用的警告股淡,但通過(guò)測(cè)試發(fā)現(xiàn),大部分系統(tǒng)提供block也是需要弱引用的__weak typeof(self) weakSelf = self;
項(xiàng)目中除了AFN的第三方組件在調(diào)用block時(shí)都是需要弱引用的廷区。
3.NSTimer
- NSTimer在釋放前唯灵,一定要調(diào)用[timer invalidate],不調(diào)用的后果就是NSTimer無(wú)法釋放其target隙轻,如果target正好是self埠帕,則會(huì)導(dǎo)致引用循環(huán)。
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay
官方文檔是這樣說(shuō)的:
This method sets up a timer to perform the aSelector message on the current thread’s run loop. The timer is configured to run in the default mode (NSDefaultRunLoopMode). When the timer fires, the thread attempts to dequeue the message from the run loop and perform the selector. It succeeds if the run loop is running and in the default mode; otherwise, the timer waits until the run loop is in the default mode.
大概意思是系統(tǒng)依靠一個(gè)timer來(lái)保證延時(shí)觸發(fā)玖绿,但是只有在runloop在default mode的時(shí)候才會(huì)執(zhí)行成功敛瓷,否則selector會(huì)一直等待run loop切換到default mode。根據(jù)我們之前關(guān)于timer
的說(shuō)法斑匪,在這里其實(shí)調(diào)用performSelector:afterDelay:同樣會(huì)造成系統(tǒng)對(duì)target強(qiáng)引用呐籽,也即retain住。這樣子蚀瘸,如果selector一直無(wú)法執(zhí)行的話(比如runloop不是運(yùn)行在default model下),這樣子同樣會(huì)造成target一直無(wú)法被釋放掉狡蝶,發(fā)生內(nèi)存泄露。怎么解決這個(gè)問(wèn)題呢贮勃?其實(shí)很簡(jiǎn)單牢酵,我們?cè)谶m當(dāng)?shù)臅r(shí)候取消掉該調(diào)用就行了,系統(tǒng)提供了接口:
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget
?這里要補(bǔ)充一點(diǎn)衙猪,引用循環(huán)不是只能有兩個(gè)對(duì)象馍乙,三個(gè)四個(gè)更多都是可以的,甚至環(huán)數(shù)也不一定只有一個(gè)垫释,所以要養(yǎng)成良好的代碼習(xí)慣丝格,在NSTimer停用前調(diào)用invalidate方法。