由于種種原因,簡書等第三方平臺博客不再保證能夠同步更新,歡迎移步 GitHub:https://github.com/kingcos/Perspective/。謝謝!
探究 iOS 中 UIImage 中兩種不同 Initializers 帶來的內存問題
本文使用 Swift 版本為 3.0 beta。
- Info:
- Swift 3.0 beta
- Xcode 8.0 beta 5
- macOS Sierra Public Beta 3
前言
近幾天接觸 iOS 的 UI 學習,由于 Swift 更新太快太頻繁,因此看的是 Obj-C 的項目,自己修改為 Swift 3.0 beta 可編譯運行的版本。Xcode 8.0 隨著幾個版本的更迭也趨于穩定,不過在不同 beta 版中,Swift 3.0 beta 的語法也略有改動,之前整理的 初探 Swift 3.0 系列也會略有過時了。這次是制作一個簡單的圖片瀏覽器,而且會動態的展示(偽) GIF 動畫,因此需要處理 animation 以及多張圖片。如果不進行適當的內存管理,那么大量的圖片占用內存,將使得程序在 iOS 設備上崩潰,造成極差的用戶體驗。因此本文淺顯地分析兩種不同的 UIImage
初始化方法,為以后的使用做以適當鋪墊。
不過,鑒于剛剛入門 iOS,所以某些行為或說法也許不太得當,需要學習的地方還有很多,故若有紕漏,還望指出。
init?(named: String)
Loading and Caching Images
init?(named: String)
Returns the image object associated with the specified filename.
Discussion
This method looks in the system caches for an image object with the specified name and returns the variant of that image that is best suited for the main screen. If a matching image object is not already in the cache, this method locates and loads the image data from disk or from an available asset catalog, and then returns the resulting object.
The system may purge cached image data at any time to free up memory. Purging occurs only for images that are in the cache but are not currently being used.
In iOS 9 and later, this method is thread safe.
Special Considerations
If you have an image file that will only be displayed once and wish to ensure that it does not get added to the system's cache, you should instead create your image usingimageWithContentsOfFile:
. This will keep your single-use image out of the system image cache, potentially improving the memory use characteristics of your app.
加載并緩存圖像
init?(named: String)
返回指定文件名所關聯的圖像對象。
論述
該方法在系統緩存中尋找指定名稱的圖像對象,并返回最適合主屏幕的圖像的變體。若對應的圖像對象已不存在于緩存,則該方法將在磁盤或存在的資源目錄中定位并載入圖像數據,并返回結果對象。
系統可能隨時清空圖像數據以釋放內存。清空操作只會發生在處于緩存但當前未被使用的圖像。
在 iOS 9 及以上版本,該方法是線程安全的。
特殊考慮
如果圖像文件只需顯示一次,且希望確保其不會被添加到系統的緩存中,你應當使用imageWithContentsOfFile:
方法。該方法將保證其在系統圖像緩存以外的單一使用,潛在提升 app 的內存使用特性。
init?(contentsOfFile: String)
Creating and Initializing Image Objects
init?(contentsOfFile: String)
Initializes and returns the image object with the contents of the specified file.
Discussion
This method loads the image data into memory and marks it as purgeable. If the data is purged and needs to be reloaded, the image object loads that data again from the specified path.
創建并初始化圖像對象
init?(contentsOfFile: String)
初始化并返回指定文件內容的圖像對象。
論述
該方法加載圖像數據到內存,并標記其可以被清空。如果數據被清空,需要重新載入,圖像對象將會從指定路徑再次加載數據。
Demo
如蘋果官方文檔所述,這兩個 Initializer 雖然都是加載圖片,但是一個做了緩存,另一個并沒有。所以 init?(named: String)
更適合加載 icon 等占用小的圖片,而 init?(contentsOfFile: String)
適合較大的圖片。
// init?(named: String)
let demoIcon = UIImage(named: "DemoIcon")
// init?(contentsOfFile: String)
let demoImagePath = Bundle.main.path(forResource: "DemoImage.png", ofType: nil)
let demoImage = UIImage(contentsOfFile: demoImagePath ?? "")
init?(named: String)
常用于加載小且常用的 icon,其初始化的圖片,占用的緩存只會在程序退出時才清空,即使消除強引用仍會占用緩存。而 init?(contentsOfFile: String)
初始化的圖片,在沒有強引用時便會自動銷毀。
而 init?(contentsOfFile: String)
使用有一些注意點。該方法的參數是圖片的全路徑,所以需要通過 Bundle 來獲取,而且需要帶上后綴名。需要注意的是,如果圖片放置在 Assets.xcassets
中,Bundle 是無法獲取到的,需要直接復制到項目中。否則的話 demoImagePath
將為 nil
,導致無法獲取到圖片,程序也將可能崩潰。
參考資料
Xcode Documentation & API Reference