背景:
前段時間做微信小程序分享,用了某家的SDK,然鵝......他們家SDK只能上傳png、jpeg格式的圖片,微信不是可以上傳Data嗎????

我吭哧吭哧半天用
UIImageJPEGRepresentation壓縮圖片,然后在生成圖片,也沒把圖片傳上去。我當(dāng)時想肯定是圖片大小有問題,因?yàn)槲⑿畔拗?28KB以內(nèi)。我查看保存在沙盒里的圖片才32KB啊??怎么會上傳不上去呢?再查看Image的Data大小,噗~~~168KB。好吧,我被打敗了。最后還是用微信原生SDK才搞定,直接傳一個Data過去,多開心,多easy。
正文
好的,扯了這么多,其實(shí)就是想說一下為啥會有今天這篇大水文。在解決問題的過程中,我對iOS加載圖片的理解稍微深入了那么一丟丟。現(xiàn)在,就水一下我理解的那么一丟丟東西。
圖片經(jīng)過哪些流程加載到屏幕上
- 從磁盤拷貝數(shù)據(jù)到內(nèi)核緩沖區(qū)
- 從內(nèi)核緩沖區(qū)復(fù)制數(shù)據(jù)到用戶空間(內(nèi)存級別拷貝)
- 生成
UIImage,把UIImage賦值給UIImageView - 如果圖像數(shù)據(jù)為未解碼的PNG/JPG,解碼為位圖數(shù)據(jù)
- 隱式
CATransaction捕獲到UIImageView圖層樹的變化 - 主線程
Runloop提交CATransaction,開始進(jìn)行圖像渲染
6.1 如果數(shù)據(jù)沒有字節(jié)對齊,Core Animation會再拷貝一份數(shù)據(jù),進(jìn)行字節(jié)對齊
6.2 GPU處理位圖數(shù)據(jù),進(jìn)行渲染
其中第四點(diǎn)就是導(dǎo)致我32KB變168KB的“罪魁禍?zhǔn)住?。為啥這么說呢?先了解一些東西。
PNG
PNG只支持無損壓縮,所以它的壓縮比是有上限的。它有alpha通道,支持圖片透明。此外xcode會對png格式進(jìn)行特殊的優(yōu)化處理,而對于其他圖片不做處理,所以我們一些小圖標(biāo)經(jīng)常用PNG。
JPEG
JPEG支持有損壓縮,不含有alpha通道,它可以通過圖片質(zhì)量換取內(nèi)存空間。網(wǎng)絡(luò)圖片最好選用JPEG,可以節(jié)省流量、提高下載速度。
位圖
我們是否可以直接使用圖片,使其顯示在屏幕上呢?答案顯然后不可以。圖片經(jīng)過解壓后,變成位圖數(shù)據(jù)。那么位圖是什么呢?蘋果給出的解釋是
A bitmap image (or sampled image) is an array of pixels (or samples)
位圖是一個像素?cái)?shù)組。至于怎么將像素繪制到屏幕上,可以看這篇文章,就不做過多敘述(人家說的很明白)。
解碼
解碼其實(shí)就是將圖片的二進(jìn)制數(shù)據(jù)轉(zhuǎn)換成像素?cái)?shù)據(jù)。這個過程是比較耗時的,不能使用 GPU 硬解碼,只能通過 CPU 軟解碼實(shí)現(xiàn)(硬解碼是通過解碼電路實(shí)現(xiàn),軟解碼是通過解碼算法、CPU 的通用計(jì)算等方式實(shí)現(xiàn)軟件層面的解碼,效率不如 GPU 硬解碼)。解碼后的文件大小計(jì)算公式
解壓縮后的圖片大小 = 圖片的像素寬 * 圖片的像素高 * 每個像素所占的字節(jié)數(shù) (4)
每個像素所占的字節(jié)數(shù)為什么是4呢?因?yàn)槲覀兯褂玫奈粓D大部分是32位的RGBA模式,這種模式位圖的一個像素所占內(nèi)存為32位,也就是4個字節(jié)的長度 。出處在此
所以,本地保存的32KB的圖片,解碼就是168KB了。(解壓縮后的數(shù)據(jù))

壓縮圖片
不過分享某一張圖片的時候,我用UIImageJPEGRepresentation方法壓縮不到128KB一下???什么圖片這么大?后來問一下后臺才知道,這張圖片是相機(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
}
圖片加載
通常我們說圖片加載會用到兩種方法:imageNamed、imageWithContentsOfFile,我們簡單介紹這兩種方法
imageNamed
該方法的特點(diǎn)在于可以緩存已經(jīng)加載的圖片;使用時,先根據(jù)文件名在系統(tǒng)緩存中尋找圖片,如果找到了就返回;如果沒有,就在Bundle內(nèi)查找到文件名,找到后把這個文件名放到UIImage里返回,并沒有進(jìn)行實(shí)際的文件讀取和解碼。當(dāng)UIImage第一次顯示到屏幕上時,其內(nèi)部的解碼方法才會被調(diào)用,同時解碼結(jié)果會保存到一個全局緩存去。在圖片解碼后,App 第一次退到后臺和收到內(nèi)存警告時,該圖片的緩存才會被清空,其他情況下緩存會一直存在。
imageWithContentsOfFile
該方法僅加載圖片,不緩存圖像數(shù)據(jù),其解碼依然要等到第一次顯示該圖片的時候。
對于這兩種方法,我們可以做出如下比較:
- 本地(Assets)保存的圖標(biāo)加載使用
imageNamed - 經(jīng)常使用且文件不大的圖片使用
imageNamed - 對于一些文件較大的圖片使用
imageWithContentsOfFile,當(dāng)然最好的辦法是用UIGraphicsBeginImageContext方法重新繪制圖片
此外,在 WWDC 2018上,蘋果為我們建議了一種大家平時使用較少的大圖加載方式,它的實(shí)際占用內(nèi)存與理論值最為接近。
func downsample(imageAt imageURL: URL, to pointSize: CGSize, scale: CGFloat) -> UIImage
{
let sourceOpt = [kCGImageSourceShouldCache : false] as CFDictionary
// 其他場景可以用createwithdata (data并未decode,所占內(nè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)存對比和最佳實(shí)踐