第一次使用Leak對項目進行內存泄漏的檢測,也是查閱了許多資料,算是可以做到發現問題並解決問題。
我的項目泄漏情況是這樣的:
滿屏的紅叉讓人心慌,那麼如何找到泄露的位置呢?
首先要選中滿是紅叉的那一行,然後在這裏
選中Call Tree,
在右邊
選中Invert Call Tree 和 Hide System Libraries 兩項,Invert Call Tree 的意思是翻轉調用樹,意思就是我們在調用函數時,是一層一層的,調用外層函數會一直進入內層,直到最後一層,有點遞歸的意思,當選中 Invert Call Tree 選項時,會直接顯示內層函數,方便我們去尋找,否則會直接顯示最外層的函數,我們需要將其一層一層展開,比較費勁,不直觀。
而Hide System Libraries 的意思很明顯了。就是隱藏系統類庫,避免一些莫名其妙的,我們無法改動的信息迷惑我們。
那麼接下來我們將看到泄漏列表:
看到這裏我震驚了,強大的AFNetworking也會存在泄露?
讓我們再看看具體是哪裏除了問題,雙擊那一行我們就可以進入到具體泄漏的那個函數
可以看到每一行泄漏的byte大小都標了出來,其中藍色的爲最大。
這裏就要思考一個問題了,AFNetworking內部的這些代碼我們怎麼改呢?
不必擔心,我們發現問題出在這個方法
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
一直以來,我都以爲這是一個manager是單例,帶你進去一看其實不然,
每次調用時都會創建一個新的對象,那麼問題又來了,創建就創建唄,難道執行之後,還會不釋放?
查閱資料後果真如此,我們使用的 AFHTTPSessionManager 繼承自 AFURLSessionManager ,
創建對象時會調用傅父類的方法。
點進去看,發現其強引用一個NSURLSession對象
並且將自己設置爲了NSURLSession對象的代理
而NSURLSession又是強引用代理
這樣便造成了循環引用,彼此誰也釋放不了。
那麼如何解決呢?
這裏提供兩種策略:
(一)NSURLSession提供兩個方法:
這個方法會立刻取消當前任務,session對象被釋放。那麼循環引用不復存在。
而finishTasksAndInvalidate方法,則會等待任務完成時將session釋放,消除了循環引用。
我們可以在success 和 failure block中調用這兩個方法,個人推薦使用finishTasksAndInvalidate。
(二)像AFNetworking 3.0 提供的DEMO中,是這麼用的:
創建一個繼承自NSHTTPSessionManager的類,實現單例方法。
然後在自己封裝的網絡層中修改。