先說(shuō)我遇到的此問(wèn)題的場(chǎng)景

示例
如圖,現(xiàn)在這種客戶端跳網(wǎng)頁(yè),上方會(huì)有個(gè)加載進(jìn)度條樣式已經(jīng)很普遍了.市面上幾乎所有的app都是這個(gè)樣式了.
- 可就在我某次無(wú)聊把玩項(xiàng)目打發(fā)時(shí)間時(shí),偶然發(fā)現(xiàn)此ViewController會(huì)概率發(fā)生pop到上級(jí)頁(yè)面后不釋放問(wèn)題,沒(méi)有走dealloc方法 -_-!!!
- 內(nèi)存泄漏可是項(xiàng)目大忌啊,好怕怕 *_*.發(fā)現(xiàn)了問(wèn)題就得改啊.
經(jīng)過(guò)多次測(cè)試把玩發(fā)現(xiàn)終于找到重現(xiàn)規(guī)律:
1. 就是在跳轉(zhuǎn)H5頁(yè)面后
2. 在webView還未完全加載完成,上方動(dòng)畫條progress還未走到100%時(shí)
3. 返回到上級(jí)頁(yè)面后,該ViewController就會(huì)出現(xiàn)內(nèi)存泄漏的問(wèn)題
- 我的bug修復(fù)之旅:
- 起初我以為是webView的問(wèn)題,折騰了很長(zhǎng)時(shí)間發(fā)現(xiàn)無(wú)果.結(jié)果是webView最終釋放了,但是控制器仍然沒(méi)有釋放.(當(dāng)然webView使用不當(dāng),也會(huì)造成內(nèi)存泄漏,這不是本次的討論重點(diǎn))
- 于是,我把思路轉(zhuǎn)向第二個(gè)必現(xiàn)條件progress動(dòng)畫上.可是動(dòng)畫那一塊的代碼看去看來(lái)也不覺(jué)得有問(wèn)題啊.
- 最后我猜可能是block使用不當(dāng),也許是哪一塊出現(xiàn)了循環(huán)引用導(dǎo)致的.最后結(jié)果是,大把的時(shí)光飛去,那個(gè)背鍋的block代碼塊并沒(méi)有被我找到.
- 就在我一籌莫展之際,突然靈光一閃,我之所以懷疑block的更本原因是循環(huán)引用.那此控制器不被釋放定然也是被什么給強(qiáng)引用了一下,pop到上級(jí)頁(yè)面的時(shí)候,它沒(méi)有釋放,導(dǎo)致了當(dāng)前頁(yè)面也沒(méi)有釋放.于是我又把目光投向了第二步
- 只有動(dòng)畫未完成時(shí),才會(huì)內(nèi)存泄漏,那就應(yīng)該是animation對(duì)象強(qiáng)行持有了當(dāng)前控制器.再次查看代碼:
animation.duration = duration;
animation.autoreverses = NO;
animation.toValue = [NSValue valueWithCGRect:frame];
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeForwards;
animation.delegate = self;
[self.progressView.layer addAnimation:animation forKey:@"progress"];
這里唯一一處引用了當(dāng)前控制器的代碼animation.delegate = self;于是我點(diǎn)開delegate進(jìn)入看了下
@property(nullable, strong) id <CAAnimationDelegate> delegate;
額!!! 官方給出此屬性定義的是strong類型,我們平時(shí)在開發(fā)時(shí),為了防止內(nèi)存泄漏,都定義的是weak或者assign.但是這里給出的是strong.也難怪我第一次看到這段代碼的時(shí)候直接就忽略掉它了.坑呀
- 找到了原因,問(wèn)題就好解決了.我在控制器生命周期中某個(gè)適當(dāng)?shù)沫h(huán)節(jié)添加了這句代碼
[self.progressView.layer removeAllAnimations];
這樣就ok了,記得適當(dāng)時(shí)機(jī)移除掉動(dòng)畫.animation釋放了,self自然就不再被持有了.
- 但是出于好奇為何animation的delegate用的是strong來(lái)定義,之前一直沒(méi)注意.最后查閱了相關(guān)資料才有所了解
- 首先動(dòng)畫是異步的,在動(dòng)畫的過(guò)程中,它的 delegate 隨時(shí)都有可能被釋放掉,如果不是個(gè)強(qiáng)引用的話,比如用戶點(diǎn)了返回之類的。另外一方面,一般來(lái)說(shuō)你并不會(huì)持有一個(gè) CAAnimation 的強(qiáng)引用(跟 UITableView 不一樣)。文檔里也說(shuō)這是內(nèi)存管理規(guī)則中的一個(gè)例外。
自己遇到的問(wèn)題,拿出來(lái)總結(jié)一下.