一張圖片引發(fā)的思考

背景:

前段時(shí)間做微信小程序分享肤粱,用了某家的SDK厨相,然鵝......他們家SDK只能上傳png领铐、jpeg格式的圖片,微信不是可以上傳Data嗎绪撵?音诈??褥傍?

???.jpeg

我吭哧吭哧半天用UIImageJPEGRepresentation壓縮圖片喇聊,然后在生成圖片誓篱,也沒(méi)把圖片傳上去。我當(dāng)時(shí)想肯定是圖片大小有問(wèn)題锦募,因?yàn)槲⑿畔拗?28KB以內(nèi)邻遏。我查看保存在沙盒里的圖片才32KB啊赎线?糊饱?怎么會(huì)上傳不上去呢?再查看ImageData大小矫废,噗~~~168KB砰蠢。好吧台舱,我被打敗了。最后還是用微信原生SDK才搞定竞惋,直接傳一個(gè)Data過(guò)去拆宛,多開(kāi)心,多easy股耽。

正文

好的钳幅,扯了這么多,其實(shí)就是想說(shuō)一下為啥會(huì)有今天這篇大水文诬乞。在解決問(wèn)題的過(guò)程中钠导,我對(duì)iOS加載圖片的理解稍微深入了那么一丟丟”菜現(xiàn)在,就水一下我理解的那么一丟丟東西换衬。

圖片經(jīng)過(guò)哪些流程加載到屏幕上

  1. 從磁盤拷貝數(shù)據(jù)到內(nèi)核緩沖區(qū)
  2. 從內(nèi)核緩沖區(qū)復(fù)制數(shù)據(jù)到用戶空間(內(nèi)存級(jí)別拷貝)
  3. 生成UIImage证芭,把UIImage賦值給UIImageView
  4. 如果圖像數(shù)據(jù)為未解碼的PNG/JPG,解碼為位圖數(shù)據(jù)
  5. 隱式CATransaction捕獲到UIImageView圖層樹(shù)的變化
  6. 主線程Runloop提交CATransaction叫潦,開(kāi)始進(jìn)行圖像渲染
    6.1 如果數(shù)據(jù)沒(méi)有字節(jié)對(duì)齊矗蕊,Core Animation會(huì)再拷貝一份數(shù)據(jù),進(jìn)行字節(jié)對(duì)齊
    6.2 GPU處理位圖數(shù)據(jù)傻咖,進(jìn)行渲染

其中第四點(diǎn)就是導(dǎo)致我32KB變168KB的“罪魁禍?zhǔn)住鼻洳佟樯哆@么說(shuō)呢?先了解一些東西扇雕。

PNG

PNG只支持無(wú)損壓縮窥摄,所以它的壓縮比是有上限的崭放。它有alpha通道慷荔,支持圖片透明。此外xcode會(huì)對(duì)png格式進(jìn)行特殊的優(yōu)化處理荷逞,而對(duì)于其他圖片不做處理迹缀,所以我們一些小圖標(biāo)經(jīng)常用PNG祝懂。

JPEG

JPEG支持有損壓縮拘鞋,不含有alpha通道,它可以通過(guò)圖片質(zhì)量換取內(nèi)存空間盆色。網(wǎng)絡(luò)圖片最好選用JPEG隔躲,可以節(jié)省流量、提高下載速度仅父。

位圖

我們是否可以直接使用圖片,使其顯示在屏幕上呢耗溜?答案顯然后不可以省容。圖片經(jīng)過(guò)解壓后,變成位圖數(shù)據(jù)。那么位圖是什么呢?蘋果給出的解釋是

A bitmap image (or sampled image) is an array of pixels (or samples)

位圖是一個(gè)像素?cái)?shù)組寞酿。至于怎么將像素繪制到屏幕上脱柱,可以看這篇文章榨为,就不做過(guò)多敘述(人家說(shuō)的很明白)。

解碼

解碼其實(shí)就是將圖片的二進(jìn)制數(shù)據(jù)轉(zhuǎn)換成像素?cái)?shù)據(jù)日川。這個(gè)過(guò)程是比較耗時(shí)的矩乐,不能使用 GPU 硬解碼,只能通過(guò) CPU 軟解碼實(shí)現(xiàn)(硬解碼是通過(guò)解碼電路實(shí)現(xiàn)分歇,軟解碼是通過(guò)解碼算法职抡、CPU 的通用計(jì)算等方式實(shí)現(xiàn)軟件層面的解碼误甚,效率不如 GPU 硬解碼)。解碼后的文件大小計(jì)算公式

解壓縮后的圖片大小 = 圖片的像素寬 * 圖片的像素高 * 每個(gè)像素所占的字節(jié)數(shù) (4)

每個(gè)像素所占的字節(jié)數(shù)為什么是4呢蹄胰?因?yàn)槲覀兯褂玫奈粓D大部分是32位的RGBA模式裕寨,這種模式位圖的一個(gè)像素所占內(nèi)存為32位,也就是4個(gè)字節(jié)的長(zhǎng)度 捻艳。出處在此
所以庆猫,本地保存的32KB的圖片月培,解碼就是168KB了。(解壓縮后的數(shù)據(jù))

恍然大悟.jpg

壓縮圖片

不過(guò)分享某一張圖片的時(shí)候纪蜒,我用UIImageJPEGRepresentation方法壓縮不到128KB一下此叠??猬错?什么圖片這么大茸歧?后來(lái)問(wèn)一下后臺(tái)才知道,這張圖片是相機(jī)拍攝的析校,尺寸非常大铜涉,只能重新設(shè)置圖片尺寸芙代。獻(xiàn)上我的代碼


func compressImage(_ image: UIImage, toByte maxLength: Int) -> Data?{
    var compression: CGFloat = 1

    var data = UIImageJPEGRepresentation(image, compression)!
    if data.count <= maxLength {
        return data
    }

    var max: CGFloat = 1
    var min: CGFloat = 0
    
    let newSize = CGSize.init(width: 200, height: 160)
    UIGraphicsBeginImageContext(newSize)
    image.draw(in: CGRect.init(x: 0, y: 0, width: newSize.width, height: newSize.height))
    let newImage = UIGraphicsGetImageFromCurrentImageContext()!
    UIGraphicsEndImageContext()
    data = UIImageJPEGRepresentation(newImage, 1.0)!
    if data.count <= maxLength {
        return data
    }

    for _ in 0..<10 {
        compression = (max + min) / 2
        data = UIImageJPEGRepresentation(newImage, compression)!
        
        if CGFloat(data.count) < CGFloat(maxLength) * 0.9 {
            min = compression
        } else if data.count > maxLength {
            max = compression
        } else {
            break
        }
    }
 
    return data
}

圖片加載

通常我們說(shuō)圖片加載會(huì)用到兩種方法:imageNamed纹烹、imageWithContentsOfFile,我們簡(jiǎn)單介紹這兩種方法

imageNamed

該方法的特點(diǎn)在于可以緩存已經(jīng)加載的圖片裹驰;使用時(shí)幻林,先根據(jù)文件名在系統(tǒng)緩存中尋找圖片,如果找到了就返回躏敢;如果沒(méi)有整葡,就在Bundle內(nèi)查找到文件名,找到后把這個(gè)文件名放到UIImage里返回啼器,并沒(méi)有進(jìn)行實(shí)際的文件讀取和解碼端壳。當(dāng)UIImage第一次顯示到屏幕上時(shí)鼠次,其內(nèi)部的解碼方法才會(huì)被調(diào)用腥寇,同時(shí)解碼結(jié)果會(huì)保存到一個(gè)全局緩存去觅捆。在圖片解碼后,App 第一次退到后臺(tái)和收到內(nèi)存警告時(shí)掂摔,該圖片的緩存才會(huì)被清空赢赊,其他情況下緩存會(huì)一直存在释移。

imageWithContentsOfFile

該方法僅加載圖片,不緩存圖像數(shù)據(jù)涩蜘,其解碼依然要等到第一次顯示該圖片的時(shí)候同诫。

對(duì)于這兩種方法樟澜,我們可以做出如下比較:

  • 本地(Assets)保存的圖標(biāo)加載使用imageNamed
  • 經(jīng)常使用且文件不大的圖片使用imageNamed
  • 對(duì)于一些文件較大的圖片使用imageWithContentsOfFile,當(dāng)然最好的辦法是用UIGraphicsBeginImageContext方法重新繪制圖片

此外贩猎,在 WWDC 2018上吭服,蘋果為我們建議了一種大家平時(shí)使用較少的大圖加載方式,它的實(shí)際占用內(nèi)存與理論值最為接近蝌戒。

func downsample(imageAt imageURL: URL, to pointSize: CGSize, scale: CGFloat) -> UIImage
{
    let sourceOpt = [kCGImageSourceShouldCache : false] as  CFDictionary
    // 其他場(chǎng)景可以用createwithdata (data并未decode,所占內(nèi)存沒(méi)那么大),
    let source = CGImageSourceCreateWithURL(imageURL as CFURL, sourceOpt)!
    
    let maxDimension = max(pointSize.width, pointSize.height) * scale
    let downsampleOpt = [kCGImageSourceCreateThumbnailFromImageAlways : true,
                         kCGImageSourceShouldCacheImmediately : true ,
                         kCGImageSourceCreateThumbnailWithTransform : true,
                         kCGImageSourceThumbnailMaxPixelSize : maxDimension] as CFDictionary
    let downsampleImage = CGImageSourceCreateThumbnailAtIndex(source, 0, downsampleOpt)!
    return UIImage(cgImage: downsampleImage)
}

參考

iOS圖片加載速度極限優(yōu)化—FastImageCache解析
談?wù)?iOS 中圖片的解壓縮
iOS中的圖片使用方式北苟、內(nèi)存對(duì)比和最佳實(shí)踐

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末友鼻,一起剝皮案震驚了整個(gè)濱河市彩扔,隨后出現(xiàn)的幾起案子僻爽,更是在濱河造成了極大的恐慌,老刑警劉巖敦捧,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件兢卵,死亡現(xiàn)場(chǎng)離奇詭異绪颖,居然都是意外死亡菠发,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)糜俗,“玉大人,你說(shuō)我怎么就攤上這事珠月±┑恚” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵啤挎,是天一觀的道長(zhǎng)驻谆。 經(jīng)常有香客問(wèn)我,道長(zhǎng)庆聘,這世上最難降的妖魔是什么胜臊? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮伙判,結(jié)果婚禮上象对,老公的妹妹穿的比我還像新娘。我一直安慰自己宴抚,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布菇曲。 她就那樣靜靜地躺著冠绢,像睡著了一般。 火紅的嫁衣襯著肌膚如雪羊娃。 梳的紋絲不亂的頭發(fā)上唐全,一...
    開(kāi)封第一講書(shū)人閱讀 51,165評(píng)論 1 299
  • 那天埃跷,我揣著相機(jī)與錄音蕊玷,去河邊找鬼。 笑死弥雹,一個(gè)胖子當(dāng)著我的面吹牛垃帅,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播剪勿,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼贸诚,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了厕吉?” 一聲冷哼從身側(cè)響起酱固,我...
    開(kāi)封第一講書(shū)人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎头朱,沒(méi)想到半個(gè)月后运悲,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡项钮,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年班眯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了希停。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡署隘,死狀恐怖宠能,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情磁餐,我是刑警寧澤违崇,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站崖媚,受9級(jí)特大地震影響亦歉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜畅哑,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一肴楷、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧荠呐,春花似錦赛蔫、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至媚创,卻和暖如春渗钉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背钞钙。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工鳄橘, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人芒炼。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓瘫怜,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親本刽。 傳聞我的和親對(duì)象是個(gè)殘疾皇子鲸湃,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

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

  • 繪制像素到屏幕上 answer-huang22 Mar 2014 分享文章 一個(gè)像素是如何繪制到屏幕上去的?有很多...
    阿貍旅途T恤閱讀 1,636評(píng)論 0 7
  • 卷首語(yǔ) 歡迎來(lái)到 objc.io 的第三期子寓! 這一期都是關(guān)于視圖層的暗挑。當(dāng)然視圖層有很多方面,我們需要把它們縮小到幾...
    評(píng)評(píng)分分閱讀 1,765評(píng)論 0 18
  • 秋冬之際斜友,北方的人們最經(jīng)常談?wù)摰脑掝}就是穿炸裆,因?yàn)樘鞖庾兓媚獪y(cè)冷暖不定,也許今天你還穿一件夾克蝙寨,明天就得披上厚厚的大...
    祁小祺閱讀 1,266評(píng)論 1 3
  • ——————————————————轉(zhuǎn)載自雷純鋒的技術(shù)博客 對(duì)于大多數(shù) iOS 應(yīng)用來(lái)說(shuō)晒衩,圖片往往是最占用手機(jī)內(nèi)存...
    woshishui1243閱讀 538評(píng)論 0 2
  • 為什么圖像在顯示到屏幕上之前要進(jìn)行解碼 一般我們使用的圖像是JPEG/PNG嗤瞎,這些圖像數(shù)據(jù)不是位圖,而是是經(jīng)過(guò)編碼...
    zziazm閱讀 7,606評(píng)論 1 30