iOS內存優化——OperationQueue悄悄給你挖的坑

前言

這篇文章是對以前我們的APP做過的一次內存優化的介紹,場景是在大批量(數千至上萬)的小文件(JSON、圖片)數據下載的情況,使用NSOperationQueue和dispatch_global_queue的一些注意點,有類似大批量數據的上傳或下載需求的童鞋可以參考一下。

一、基本概念

隊列的使用場景

在iOS的開發中,我們經常會需要處理一些耗時操作,比如網絡請求、文件讀寫、數據庫增刪改查等等,這時候,如果對UI沒有依賴操作,我們一般會開闢一個異步線程去做這些耗時任務。

  • 有時我們會遇到需要按順序處理一批數據的情況,它們的處理方式依然按照上面所說的方式去處理,不過這種情況下,我們就會使用operation結合queue的形式來保證它們異步按順序執行。

  • 當希望批量的耗時任務按大概的順序並較快完成時,我們可以把operation加入queue,並設置一個合理的併發數。

二、問題

  • 每個耗時的operation本身需要一個對象去承載,即記錄這次任務執行包含的必要信息,當它被加入隊列以後,這個operation對象會被queue佔有,保證任務執行完成之前***不被內存釋放***。

  • 當operation數量十分巨大且隊列處理任務的速度小於任務的添加速度時,那麼***內存峯值***就會持續上升,如果這個峯值大於系統所能給當前APP分配的可用內存時,不用想——didReceiveMemoryWarning->Terminated due to Memory Error.

三、解決方案

既然問題出在使用隊列造成了過大***內存峯值***上,那麼肯定就從降低內存峯值下手咯,降低內存峯值的直接辦法就是控制隊列的最大任務數。

  1. 將需要執行的任務信息存入硬盤(plist、database),每次取出定量的任務加入隊列處理,爲保證充分利用cpu,任務數降低到併發數之前再次從硬盤取出定量的任務加入隊列。
    這種情況適用於一些文件讀寫操作等單個文件本身需要耗費大量內存或者批量文件上傳下載的情況。

  2. 將需要執行的任務最簡單的基本信息放入內存(數組、字典),每次從內存中取出基本信息創建一個完整的operation加入隊列。數量控制同1。
    這種情況適用於網絡請求等只需要保存必須參數信息,且信息量很小,而發送網絡請求創建request operation時可以再添加基礎參數的情況。

四、場景實戰

在我工作中負責維護的一款APP中,允許用戶離線對數據進行操作,這就要求把用戶所有的數據進行緩存。

在用戶使用一個新的設備首次登錄進入APP時,我們需要下載所有的用戶數據,經測試在用戶的數據量較大時,數據緩存到一定量時,APP就因爲內存問題被系統kill了。

經過分析問題就是出在operation queue的使用上面,在queue中的待下載任務數量達到8000以上時在iPhone6上面就基本必現crash。

依照三、2中的方案對此處進行了隊列任務數量的控制,降低內存峯值在可控範圍內,基本保證用戶首次使用需要加載數據量達到數萬也不會crash。

五、拓展

在批量的文件下載中尤其需要注意:

如果文件的下載速度十分快,如果把下載任務和下載完成後的文件本地寫入任務都放入同一個異步隊列(比如全局併發隊列),且下載任務優先級高於文件本地寫入任務的優先級的情況時,有可能就會有大量的文件下載完成待寫入,會消耗大量的內存,形成較高的內存峯值。

此時解決方案可能需要結合三同時對任務的優先級做控制。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章