在 iOS 5之前使用的都是 MRC,需要程序員自己去 Retain,Release,這樣一不注意會(huì)經(jīng)常產(chǎn)生內(nèi)存泄漏問題,但是之后出現(xiàn)了 ARC,系統(tǒng)會(huì)自動(dòng)幫助我們在合適的位置添加 Retain,Release,可以說是減少了大部分的麻煩.但是在一些情況下還是會(huì)產(chǎn)生內(nèi)存泄漏,需要我們注意
那么在什么情況下會(huì)產(chǎn)生內(nèi)存泄漏呢,在 ARC 中,產(chǎn)生內(nèi)存泄漏主要是因?yàn)檠h(huán)引用,當(dāng)有循環(huán)引用的時(shí)候,導(dǎo)致誰都不能釋放,所以就會(huì)產(chǎn)生內(nèi)存泄漏.
在我們?nèi)粘i_發(fā)中最常見的就是使用 block 產(chǎn)生的循環(huán)引用,delegate 和 NSTimer 等都會(huì)產(chǎn)生循環(huán)引用的問題.
首先讓我們深度了解一下 block 的循環(huán)引用以及如何更好地處理:
?????? 當(dāng)一個(gè)類有 block 屬性,而在 block 中又引用這個(gè)類本身 self 時(shí),就會(huì)產(chǎn)生循環(huán)引用導(dǎo)致都釋放不了,形成 A -> B, B -> A兩方的循環(huán)引用.
?????? 還有一種情況,當(dāng)某各類有 block 屬性, 控制器 viewController 強(qiáng)引用這個(gè)類, 在 block 中 又調(diào)用 控制器本身時(shí),就會(huì)形成 A -> B, B -> C, C -> A,三方的循環(huán)引用.? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 這兩種情況都屬于 block 引起的循環(huán)引用,解決辦法就是讓一方變?yōu)槿跻?通常情況下使 block 對 self 變成弱引用來解決循環(huán)引用問題,很多人只會(huì)使用如下? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? __weak typedef(self) weakSelf = self;? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ???????????????????????????? ? ? ? ? ? self.block = ^(){? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? weakSelf.view.backgroundColor = [UIColor redColor];??????????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? }? ? ? ? ? ???????????????????????????????????????????????????????????????????????????????????????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? 但是這樣很可能會(huì)出現(xiàn)崩潰,因?yàn)榘?self 變成弱引用后,在它的作用域中隨時(shí)都可能被回收而變成 nil ,正確的做法是???????????????????????????????????????????????????????????????????????????????????????????????????????????????????? __weak typedef(self) weakSelf =self;???????????????????????????????????????????????????????????????????????????? self.block = ^(){??????????????????????????????????????????????????????????????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? __strong typedef(weakSelf) strongSelf = weakSelf;? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if(strongSelf) { strongSelf.view.backgroundColor = [UIColorredColor];? }}
看唐巧的文章中提到這么一個(gè)問題, 如果說 block 會(huì)產(chǎn)生循環(huán)引用,但是業(yè)務(wù)上又不能使用 weakSelf 呢?那么這種情況下應(yīng)該如何處理循環(huán)引用呢??????? ? ? ? ? ? ? ? ? ??????????????????????????????????????? 場景: 在 YTNetwork 中,每一個(gè)網(wǎng)絡(luò)請求 API 會(huì)持有回調(diào)的 block , block 會(huì)持有 self, self 也持有 網(wǎng)絡(luò)請求的 API ,這時(shí)候很明顯是一個(gè)循環(huán)引用 ,但是你有一個(gè)后臺(tái)任務(wù),希望任務(wù)執(zhí)行完后,通知另外一個(gè)實(shí)例,這時(shí)候就需要我們主動(dòng)釋放 block,方法很簡單,就是把 block 手動(dòng)置為 nil, self.block = nil;
其次就是 NSTimer 產(chǎn)生的循環(huán)引用問題
那么 NSTimer 會(huì)如何產(chǎn)生循環(huán)引用呢?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? 首先 timer 定時(shí)器要加入到 runloop 中,此時(shí) runloop 會(huì)對 timer 強(qiáng)引用,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 而 timer 注冊時(shí)又會(huì)對 targer(一般是 self 控制器) 強(qiáng)引用? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 而 self 又會(huì)對 runloop 產(chǎn)生強(qiáng)引用 ,此時(shí)就造成循環(huán)引用的問題,有時(shí)候我們我們對某個(gè)Timer的targer設(shè)置了nil。但沒設(shè)置[timer invalidate].其實(shí)這個(gè)對象還是沒被釋放的。timer對應(yīng)的執(zhí)行方法也一直會(huì)在線程中執(zhí)行。容易造成內(nèi)存泄露。
如何解決呢,即何時(shí)調(diào)用 [timer invalidate] 方法呢,有些人可能想到在 dealloc 方法中 ,但是你別忘了, dealloc 方法什么時(shí)候調(diào)用呢 ,就是當(dāng) self 釋放的時(shí)候才會(huì)調(diào)用 dealloc 方法,此時(shí)產(chǎn)生了循環(huán)引用就意味著 self 不會(huì)釋放了 ,所以在 dealloc 方法中調(diào)用將不會(huì)執(zhí)行, 我們可以考慮在 ViewWillDisAppear 方法中調(diào)用 [timer invalidate] 使定時(shí)器失效.
最后就是在 delegate 情況下產(chǎn)生的循環(huán)引用
這種情況大家都很熟悉, 就是代理屬性需要使用 weak 修飾,就可以避免循環(huán)引用問題,這里就不詳細(xì)介紹了.