如何解決UIImage加載大圖,內(nèi)存過(guò)大導(dǎo)致Carsh?

在本文中,與您分享我們面臨的問(wèn)題,高內(nèi)存使用率的根本原因是什么便斥,以及我們?nèi)绾问褂?Apple 工程師推薦的簡(jiǎn)單修復(fù)來(lái)減少應(yīng)用程序的內(nèi)存占用。

問(wèn)題

如前所述威始,當(dāng)我們開始在屏幕上加載高清圖像時(shí)枢纠,我們的應(yīng)用程序的內(nèi)存使用量會(huì)激增。為了展示我們面臨的問(wèn)題字逗,我創(chuàng)建了一個(gè)示例應(yīng)用程序京郑,當(dāng)點(diǎn)擊按鈕時(shí),它會(huì)將高清圖像加載到圖像視圖中葫掉。


初始內(nèi)存.png

    @IBAction func loadImage(_ sender: Any) {
        image.image = UIImage(named: "image.JPG")
    }

截屏2021-06-30 上午11.55.17.png
加載圖片后內(nèi)存.png

請(qǐng)注意,我使用的是尺寸為 6240?×?4160px 的圖像跟狱,其文件大小為 9MB俭厚。

如您所見,加載圖像后內(nèi)存使用量從 9MB 飆升至 155MB驶臊。

這完全沒有道理挪挤!我們正在加載一個(gè) 9MB 的圖像文件,內(nèi)存占用怎么會(huì)這么高关翎?所有這些內(nèi)存使用來(lái)自哪里扛门???

內(nèi)存使用 ≠ 文件大小

我們用來(lái)解決這個(gè)問(wèn)題的一種方法是縮小圖像并根據(jù)圖像視圖的幀大小將其繪制在屏幕上,不幸的是纵寝,這似乎并沒有解決我們的問(wèn)題论寨。

在沒有任何關(guān)于如何解決問(wèn)題的線索的情況下,我轉(zhuǎn)向了網(wǎng)絡(luò)。在谷歌搜索了幾次之后葬凳,我得到了一個(gè)驚人的 WWDC 視頻绰垂,它引入了一個(gè)我們都不知道的非常重要的概念:

內(nèi)存使用與圖像的尺寸有關(guān),與文件大小無(wú)關(guān)火焰。

為了在屏幕上顯示圖像劲装,iOS 首先需要對(duì)圖像進(jìn)行解碼和解壓縮。通常昌简,解碼圖像的 1 個(gè)像素將占用 4 個(gè)字節(jié)的內(nèi)存

  • 1 個(gè)字節(jié)用于紅色占业,
  • 1 個(gè)字節(jié)用于綠色,
  • 1 個(gè)字節(jié)用于藍(lán)色纯赎,
  • 1 個(gè)字節(jié)用于 alpha 分量谦疾。

以我們尺寸為 6240?×?4160px 的示例圖片為例:

(6240?×?4160) * 4字節(jié) = 103833600 bytes  
差不多 103.8336 MB

這確實(shí)與我們?cè)趦?nèi)存報(bào)告中看到的想對(duì)接近一點(diǎn)了(由于圖片場(chǎng)景格式問(wèn)題,會(huì)有所偏差)

降采樣來(lái)拯救

我們已經(jīng)弄清楚內(nèi)存使用的來(lái)源,但是我們?nèi)绾螌?nèi)存占用減少到可接受的水平呢址否?

正如我之前提到的餐蔬,我們嘗試縮小并重新繪制圖像,但這似乎沒有幫助佑附。這是因?yàn)?code>UIImage調(diào)整大小和調(diào)整大小很昂貴樊诺。在調(diào)整大小的過(guò)程中,iOS 仍然會(huì)解碼和解壓縮原始圖像音同,從而導(dǎo)致不必要的內(nèi)存峰值词爬。

使用 ImageIO 進(jìn)行下采樣

幸運(yùn)的是,WWDC18 為我們面臨的問(wèn)題提供了很好的解決方案权均。我們可以使用該ImageIO框架在圖像顯示在屏幕上之前調(diào)整其大小顿膨。這種ImageIO方法的偉大之處在于,我們現(xiàn)在只需支付調(diào)整大小圖像的成本就可以調(diào)整圖像大小叽赊。我們稱這種方法為“下采樣”恋沃。

以下代碼片段演示了如何對(duì)圖像執(zhí)行下采樣:

func downsample(imageAt imageURL: URL,
                to pointSize: CGSize,
                scale: CGFloat = UIScreen.main.scale) -> UIImage? {

    // Create an CGImageSource that represent an image
    let imageSourceOptions = [kCGImageSourceShouldCache: false] as CFDictionary
    guard let imageSource = CGImageSourceCreateWithURL(imageURL as CFURL, imageSourceOptions) else {
        return nil
    }
    
    // Calculate the desired dimension
    let maxDimensionInPixels = max(pointSize.width, pointSize.height) * scale
    
    // Perform downsampling
    let downsampleOptions = [
        kCGImageSourceCreateThumbnailFromImageAlways: true,
        kCGImageSourceShouldCacheImmediately: true,
        kCGImageSourceCreateThumbnailWithTransform: true,
        kCGImageSourceThumbnailMaxPixelSize: maxDimensionInPixels
    ] as CFDictionary
    guard let downsampledImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, downsampleOptions) else {
        return nil
    }
    
    // Return the downsampled image as UIImage
    return UIImage(cgImage: downsampledImage)
}

讓我們先來(lái)看看函數(shù)的參數(shù):

  • imageURL:圖片網(wǎng)址。它可以是 Web URL 或本地圖像路徑必指。
  • pointSize:下采樣圖像的所需大小囊咏。通常,這將是UIImageView的幀大小塔橡。
  • scale:下采樣比例因子梅割。通常,這將是與屏幕相關(guān)的比例因子(我們通常將其稱為@2x或@3x)葛家。這就是為什么您可以看到其默認(rèn)值已設(shè)置為UIScreen.main.scale.

選項(xiàng)標(biāo)志

接下來(lái)户辞,我想提請(qǐng)你注意選項(xiàng)標(biāo)志3的功能被使用- kCGImageSourceShouldCachekCGImageSourceShouldCacheImmediatelykCGImageSourceCreateThumbnailWithTransform癞谒。

首先底燎,讓我們談?wù)?code>kCGImageSourceShouldCache刃榨。當(dāng)此標(biāo)志設(shè)置為 false 時(shí),我們讓核心圖形框架知道我們只需要?jiǎng)?chuàng)建對(duì)圖像源的引用书蚪,并且不想CGImageSource在創(chuàng)建對(duì)象時(shí)立即解碼圖像喇澡。

提示:在您無(wú)法訪問(wèn)圖像源路徑的情況下,您可以CGImageSource使用CGImageSourceCreateWithData()初始化程序創(chuàng)建一個(gè)對(duì)象殊校。

列表中的下一個(gè)是kCGImageSourceShouldCacheImmediately晴玖。這個(gè)標(biāo)志表示核心圖形框架應(yīng)該在我們開始下采樣過(guò)程的那一刻解碼圖像。

因此为流,通過(guò)同時(shí)使用kCGImageSourceShouldCachekCGImageSourceShouldCacheImmediately選項(xiàng)標(biāo)志呕屎,我們可以完全控制何時(shí)要占用 CPU 進(jìn)行圖像解碼。

最后是kCGImageSourceCreateThumbnailWithTransform選項(xiàng)標(biāo)志敬察。將此標(biāo)志設(shè)置為 true 非常重要秀睛,因?yàn)樗尯诵膱D形框架知道您希望下采樣圖像與原始圖像具有相同的方向。

讓我們看看結(jié)果

現(xiàn)在讓我們嘗試再次在屏幕上加載我們的示例圖像莲祸,但這次啟用了下采樣蹂安。

let filePath = Bundle.main.url(forResource: "image", withExtension: "JPG")!
let downsampledLadyImage = downsample(imageAt: filePath, to: imageView.bounds.size)
imageView.image = downsampledLadyImage
最終內(nèi)存.png

你已經(jīng)看到了下采樣的結(jié)果,它非常驚人锐帜。但這并不意味著您應(yīng)該開始將高清圖像捆綁到您的應(yīng)用程序中并在需要時(shí)對(duì)它們進(jìn)行下采樣田盈。

請(qǐng)記住,下采樣是一個(gè)占用 CPU 能力的過(guò)程缴阎。因此允瞧,最好使用適當(dāng)壓縮后的圖像源,而不是對(duì) HD 圖像進(jìn)行下采樣蛮拔。
換句話說(shuō)述暂,只有當(dāng)您需要顯示尺寸遠(yuǎn)高于屏幕上所需尺寸的圖像時(shí),才應(yīng)考慮使用下采樣建炫。

本文demo Github地址: https://github.com/stevendinggang/UIImageMemory

本文參考: https://developer.apple.com/wwdc18/219

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末畦韭,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子肛跌,更是在濱河造成了極大的恐慌廊驼,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,490評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件惋砂,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡绳锅,警方通過(guò)查閱死者的電腦和手機(jī)西饵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)鳞芙,“玉大人眷柔,你說(shuō)我怎么就攤上這事期虾。” “怎么了驯嘱?”我有些...
    開封第一講書人閱讀 165,830評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵镶苞,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我鞠评,道長(zhǎng)茂蚓,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,957評(píng)論 1 295
  • 正文 為了忘掉前任剃幌,我火速辦了婚禮聋涨,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘负乡。我一直安慰自己牍白,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評(píng)論 6 393
  • 文/花漫 我一把揭開白布抖棘。 她就那樣靜靜地躺著茂腥,像睡著了一般。 火紅的嫁衣襯著肌膚如雪切省。 梳的紋絲不亂的頭發(fā)上最岗,一...
    開封第一講書人閱讀 51,754評(píng)論 1 307
  • 那天,我揣著相機(jī)與錄音数尿,去河邊找鬼仑性。 笑死,一個(gè)胖子當(dāng)著我的面吹牛右蹦,可吹牛的內(nèi)容都是我干的诊杆。 我是一名探鬼主播,決...
    沈念sama閱讀 40,464評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼何陆,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼晨汹!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起贷盲,我...
    開封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤淘这,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后巩剖,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體铝穷,經(jīng)...
    沈念sama閱讀 45,847評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評(píng)論 3 338
  • 正文 我和宋清朗相戀三年佳魔,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了曙聂。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,137評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡鞠鲜,死狀恐怖宁脊,靈堂內(nèi)的尸體忽然破棺而出断国,到底是詐尸還是另有隱情,我是刑警寧澤榆苞,帶...
    沈念sama閱讀 35,819評(píng)論 5 346
  • 正文 年R本政府宣布稳衬,位于F島的核電站,受9級(jí)特大地震影響坐漏,放射性物質(zhì)發(fā)生泄漏薄疚。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評(píng)論 3 331
  • 文/蒙蒙 一仙畦、第九天 我趴在偏房一處隱蔽的房頂上張望输涕。 院中可真熱鬧,春花似錦慨畸、人聲如沸莱坎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)檐什。三九已至,卻和暖如春弱卡,著一層夾襖步出監(jiān)牢的瞬間乃正,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工婶博, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留瓮具,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,409評(píng)論 3 373
  • 正文 我出身青樓凡人,卻偏偏與公主長(zhǎng)得像名党,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子传睹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容