iOS圖片內(nèi)存管理和性能優(yōu)化

圖片在計(jì)算機(jī)中如何存儲(chǔ)和表示?

常見(jiàn)的圖片格式

JPEG 是目前最常見(jiàn)的圖片格式,它誕生于1992年,是一個(gè)很古老的格式。它只支持有損壓縮,其壓縮算法可以精確控制壓縮比,以圖像質(zhì)量換得存儲(chǔ)空間。由于它太過(guò)常見(jiàn),以至于許多移動(dòng)設(shè)備的 CPU 都支持針對(duì)它的硬編碼與硬解碼。

PNG 誕生在 1995 年,比 JPEG 晚幾年。它本身的設(shè)計(jì)目的是替代 GIF 格式,所以它與 GIF 有更多相似的地方。PNG 只支持無(wú)損壓縮,所以它的壓縮比是有上限的。相對(duì)于 JPEG 和 GIF 來(lái)說(shuō),它最大的優(yōu)勢(shì)在于支持完整的透明通道。

GIF 誕生于 1987 年,隨著初代互聯(lián)網(wǎng)流行開(kāi)來(lái)。它有很多缺點(diǎn),比如通常情況下只支持 256 種顏色、透明通道只有 1 bit、文件壓縮比不高。它唯一的優(yōu)勢(shì)就是支持多幀動(dòng)畫(huà),憑借這個(gè)特性,它得以從 Windows 1.0 時(shí)代流行至今,而且仍然大受歡迎。

格式 優(yōu)點(diǎn) 缺點(diǎn) 用途
jpg 色彩豐富,文件小 有損壓縮 顏色豐富的圖
png 透明、無(wú)損壓縮、簡(jiǎn)單圖文件小 若顏色較多復(fù)雜,則圖片生成后的文件很大 小圖標(biāo)、透明背景
gif 動(dòng)態(tài)、透明、文件小 色域不廣、只有256種顏色 動(dòng)態(tài)圖片

除了以上面常見(jiàn)的格式,也有一些新型的格式:

APNG 是 Mozilla 在 2008 年發(fā)布的一種圖片格式,旨在替換掉畫(huà)質(zhì)低劣的 GIF 動(dòng)畫(huà)。它實(shí)際上只是相當(dāng)于 PNG 格式的一個(gè)擴(kuò)展,所以 Mozilla 一直想把它合并到 PNG 標(biāo)準(zhǔn)里面去。然而 PNG 開(kāi)發(fā)組并沒(méi)有接受 APNG 這個(gè)擴(kuò)展,而是一直在推進(jìn)它自己的 MNG 動(dòng)圖格式。MNG 格式過(guò)于復(fù)雜以至于并沒(méi)有什么系統(tǒng)或?yàn)g覽器支持,而 APNG 格式由于簡(jiǎn)單容易實(shí)現(xiàn),目前已經(jīng)漸漸流行開(kāi)來(lái)。Mozilla 自己的 Firefox 首先支持了 APNG,隨后蘋(píng)果的 Safari 也開(kāi)始有了支持, Chrome 目前也已經(jīng)嘗試開(kāi)始支持 ,可以說(shuō)未來(lái)前景很好。

APNG 與 Gif 對(duì)比

WebP 是 Google 在 2010 年發(fā)布的圖片格式,希望以更高的壓縮比替代 JPEG。它用 VP8 視頻幀內(nèi)編碼作為其算法基礎(chǔ),取得了不錯(cuò)的壓縮效果。它支持有損和無(wú)損壓縮、支持完整的透明通道、也支持多幀動(dòng)畫(huà),并且沒(méi)有版權(quán)問(wèn)題,是一種非常理想的圖片格式(美中不足的是,WebP格式圖像的編碼時(shí)間“比JPEG格式圖像長(zhǎng)8倍)。借由 Google 在網(wǎng)絡(luò)世界的影響力,WebP 在幾年的時(shí)間內(nèi)已經(jīng)得到了廣泛的應(yīng)用??纯茨闶謾C(jī)里的 App:微博、微信、QQ、淘寶、網(wǎng)易新聞等等,每個(gè) App 里都有 WebP 的身影。Facebook 則更進(jìn)一步,用 WebP 來(lái)顯示聊天界面的貼紙動(dòng)畫(huà)。

關(guān)于以上幾種圖片格式在移動(dòng)端的解碼和性能對(duì)比參見(jiàn):移動(dòng)端圖片格式調(diào)研

iOS中圖片加載過(guò)程和性能瓶頸

如上文所說(shuō),大部分格式的圖片都是被壓縮的,都需要被首先解碼為bitmap(未壓縮的位圖),然后才能渲染到UI上。
UIImageView 顯示圖片,也有類(lèi)似的過(guò)程。實(shí)際上,一張圖片從在文件系統(tǒng)中,到被顯示到 UIImageView,會(huì)經(jīng)歷以下幾個(gè)步驟:

  1. 假設(shè)我們使用 +imageWithContentsOfFile: 方法從磁盤(pán)中加載一張圖片,這個(gè)時(shí)候的圖片并沒(méi)有解壓縮;
  2. 然后將生成的 UIImage 賦值給 UIImageView
  3. 接著一個(gè)隱式的 CATransaction 捕獲到了 UIImageView 圖層樹(shù)的變化;
  4. 在主線(xiàn)程的下一個(gè) run loop 到來(lái)時(shí),Core Animation 提交了這個(gè)隱式的 transaction ,這個(gè)過(guò)程可能會(huì)對(duì)圖片進(jìn)行 copy 操作,而受圖片是否字節(jié)對(duì)齊等因素的影響,這個(gè) copy 操作可能會(huì)涉及以下部分或全部步驟:
    1. 分配內(nèi)存緩沖區(qū)用于管理文件 IO 和解壓縮操作;
    2. 將文件數(shù)據(jù)從磁盤(pán)讀到內(nèi)存中;
    3. 將壓縮的圖片數(shù)據(jù)解碼成未壓縮的位圖形式,這是一個(gè)非常耗時(shí)的 CPU 操作;
    4. 最后 Core Animation 使用未壓縮的位圖數(shù)據(jù)渲染 UIImageView 的圖層。

在上面的步驟中,我們提到了圖片的解壓縮是一個(gè)非常耗時(shí)的 CPU 操作,并且它默認(rèn)是在主線(xiàn)程中執(zhí)行的。那么當(dāng)需要加載的圖片比較多時(shí),就會(huì)對(duì)我們應(yīng)用的響應(yīng)性造成嚴(yán)重的影響,尤其是在快速滑動(dòng)的列表上,這個(gè)問(wèn)題會(huì)表現(xiàn)得更加突出。這就是 UIImageView 的一個(gè)性能瓶頸。

實(shí)際上,當(dāng)我們調(diào)用[UIImage imageNamed:@"xxx"]后,UIImage 中存儲(chǔ)的是未解碼的圖片,而調(diào)用 [UIImageView setImage:image]后,會(huì)在主線(xiàn)程進(jìn)行圖片的解碼工作并且將圖片顯示到 UI 上,這時(shí)候,UIImage 中存儲(chǔ)的是解碼后的 bitmap 數(shù)據(jù)。

為什么需要解壓縮

既然圖片的解壓縮需要消耗大量的 CPU 時(shí)間,那么我們?yōu)槭裁催€要對(duì)圖片進(jìn)行解壓縮呢?是否可以不經(jīng)過(guò)解壓縮,而直接將圖片顯示到屏幕上呢?答案是否定的。要想弄明白這個(gè)問(wèn)題,我們首先需要知道什么是位圖

bitmap:bitmap 又叫位圖文件,它是一種非壓縮的圖片格式,所以體積非常大。所謂的非壓縮,就是圖片每個(gè)像素的原始信息在存儲(chǔ)器中依次排列,一張典型的1920*1080像素的 bitmap 圖片,每個(gè)像素由 RGBA 四個(gè)字節(jié)表示顏色,那么它的體積就是 1920 * 1080 * 4 = 1012.5kb。

由于 bitmap 簡(jiǎn)單順序存儲(chǔ)圖片的像素信息,它可以不經(jīng)過(guò)解碼就直接被渲染到 UI 上。實(shí)際上,其它格式的圖片都需要先被首先解碼為 bitmap,然后才能渲染到界面上。

不管是 JPEG 還是 PNG 圖片,都是一種壓縮的位圖圖形格式。只不過(guò) PNG 圖片是無(wú)損壓縮,并且支持 alpha 通道,而 JPEG 圖片則是有損壓縮,可以指定 0-100% 的壓縮比。值得一提的是,在蘋(píng)果的 SDK 中專(zhuān)門(mén)提供了兩個(gè)函數(shù)用來(lái)生成 PNG 和 JPEG 圖片:

// return image as PNG. May return nil if image has no CGImageRef or invalid bitmap format
UIKIT_EXTERN NSData * __nullable UIImagePNGRepresentation(UIImage * __nonnull image);

// return image as JPEG. May return nil if image has no CGImageRef or invalid bitmap format. compression is 0(most)..1(least)                           
UIKIT_EXTERN NSData * __nullable UIImageJPEGRepresentation(UIImage * __nonnull image, CGFloat compressionQuality);

因此,在將磁盤(pán)中的圖片渲染到屏幕之前,必須先要得到圖片的原始像素?cái)?shù)據(jù),才能執(zhí)行后續(xù)的繪制操作,這就是為什么需要對(duì)圖片解壓縮的原因。

圖片解壓縮的過(guò)程其實(shí)就是將圖片的二進(jìn)制數(shù)據(jù)轉(zhuǎn)換成像素?cái)?shù)據(jù)的過(guò)程

圖片的編碼和解碼
iOS 底層是用 ImageIO.framework 實(shí)現(xiàn)的圖片編解碼。目前 iOS 原生支持的格式有:JPEG、JPEG2000、PNG、GIF、BMP、ICO、TIFF、PICT,自 iOS 8.0 起,ImageIO 又加入了 APNG、SVG、RAW 格式的支持。在上層,開(kāi)發(fā)者可以直接調(diào)用 ImageIO 對(duì)上面這些圖片格式進(jìn)行編碼和解碼。對(duì)于動(dòng)圖來(lái)說(shuō),開(kāi)發(fā)者可以解碼動(dòng)畫(huà) GIF 和 APNG、可以編碼動(dòng)畫(huà) GIF。

注意:圖片所占內(nèi)存的大小與圖片的尺寸有關(guān),而不是圖片的文件大小

色彩空間和像素格式

計(jì)算圖片解碼后每行需要的比特?cái)?shù),由兩個(gè)參數(shù)相乘得到:每行的像素?cái)?shù) width,和存儲(chǔ)一個(gè)像素需要的比特?cái)?shù)4

這里的4,其實(shí)是由每張圖片的像素格式和像素組合來(lái)決定的,下表是蘋(píng)果平臺(tái)支持的像素組合方式

image

)

表中的bpp,表示每個(gè)像素需要多少位;bpc表示顏色的每個(gè)分量,需要多少位。具體的解釋方式,可以看下面這張圖:

我們解碼后的圖片,默認(rèn)采用 kCGImageAlphaNoneSkipLast RGB 的像素組合,沒(méi)有 alpha 通道,每個(gè)像素32位4個(gè)字節(jié),前三個(gè)字節(jié)代表紅綠藍(lán)三個(gè)通道, 但是有時(shí)候,如果我們只是繪制一個(gè)蒙版,是不需要這么字節(jié)表示的比如 Alpha 8 format,每個(gè)像素只需要占用 1 個(gè)字節(jié),這之間的差距就造成了內(nèi)存浪費(fèi)。

UIGraphicsImageRenderer 和 UIGraphicsBeginImageContextWithOptions

當(dāng)我們?yōu)榱穗x屏渲染,要?jiǎng)?chuàng)建 image buffer 時(shí),我們通常會(huì)使用 UIGraphicsBeginImageContext,但是最好還是用 UIGraphicsImageRenderer,它的性能更好、更高效,并且支持廣色域。這里有一個(gè)中間地帶,如果你主要將圖像渲染到圖形圖像渲染器(graphic image render)中,該圖像可能使用超出 SRGB 色域的色彩空間值,但實(shí)際上并不需要更大的元素來(lái)存儲(chǔ)這些信息。所以 UIImage 有一個(gè)可以用來(lái)獲取預(yù)構(gòu)建的 UIGraphicsImageRendererFormat 對(duì)象的 image renderer format 屬性,該對(duì)象用于重新渲染圖像時(shí)進(jìn)行最優(yōu)化存儲(chǔ)。

所以蘋(píng)果官方建議使用 UIGraphicsImageRenderer,這個(gè)方法是從 iOS 10 引入,在 iOS 12 上會(huì)自動(dòng)選擇最佳的圖像格式,可以減少很多內(nèi)存。系統(tǒng)可以根據(jù)圖片分辨率選擇創(chuàng)建解碼圖片的格式,如選用SRGB format 格式,每個(gè)像素占用 4 字節(jié),而Alpha 8 format,每像素只占用 1 字節(jié),可以減少大量的解碼內(nèi)存占用。

擴(kuò)展閱讀色彩空間與像素格式

imageWithContentsOfFile 和 imageNamed 對(duì)比

imgeNamed

用這個(gè)方法加載圖片分為兩種情況:

  1. 系統(tǒng)緩存有這個(gè)圖片,直接從緩存中取得
  2. 系統(tǒng)緩存沒(méi)有這個(gè)圖片
    通過(guò)傳入的文件名對(duì)整個(gè)工程進(jìn)行遍歷 (在application bundle的頂層文件夾尋找名字的圖象 ), 如果如果找到對(duì)應(yīng)的圖片,iOS 系統(tǒng)首先要做的是將這個(gè)圖片放到系統(tǒng)緩存中去,以備下次使用的時(shí)候直接從系統(tǒng)緩存中取, 接下來(lái)重復(fù)第一步,即直接從緩存中取

由于系統(tǒng)會(huì)緩存圖片,所以如果要加載的這個(gè)圖片的文件量很多,文件大小很大,內(nèi)存不足,內(nèi)存泄露,甚至是程序的崩潰都是很容易發(fā)生的事.

imageWithContentsOfFile

用這個(gè)方法只有一種情況,那就是僅僅加載圖片, 圖像數(shù)據(jù)不會(huì)被緩存. 因此在加載較大圖片的時(shí)候, 以及圖片使用情況很少的時(shí)候可以使用這兩個(gè)方法 , 降低內(nèi)存消耗.

加載本地圖片,要比從Assets Catalogs耗時(shí)要長(zhǎng),具體見(jiàn)Assets Catalogs 與 I/O 優(yōu)化

圖片內(nèi)存優(yōu)化

到此我們可知,圖片經(jīng)過(guò)解壓之后,在內(nèi)存中實(shí)際是根據(jù)圖片的分辨率和圖片渲染所用的像素格式而定的

一、對(duì)不常用的大圖片,使用 imageWithContentsOfFile 代替 imageNamed 方法,避免內(nèi)存緩存(相應(yīng)的使用imageNamed要避免載入大量的圖片造成內(nèi)存暴增)

二、使用 ImageIO 方法,對(duì)大圖片進(jìn)行縮放,減少圖片解碼占用內(nèi)存大小。

UIImage 在設(shè)置和調(diào)整大小的時(shí)候,需要將原始圖像加壓到內(nèi)存中,然后對(duì)內(nèi)部坐標(biāo)空間做一系列轉(zhuǎn)換,整個(gè)過(guò)程會(huì)消耗很多資源。我們可以使用 ImageIO,它可以直接讀取圖像大小和元數(shù)據(jù)信息,不會(huì)帶來(lái)額外的內(nèi)存開(kāi)銷(xiāo)。

三、繪制圖片,用 UIGraphicsImageRenderer 代替 UIGraphicsBeginImageContextWithOptions,自動(dòng)管理顏色格式

四、超大圖片處理

  1. 加載使用蘋(píng)果推薦的DownSampling方案(縮略圖方式)
     // DownSampling(降低采樣)
     // 在視圖比較小,圖片比較大的場(chǎng)景下,直接展示原圖片會(huì)造成不必要的內(nèi)存和CPU消耗,這里就可以使用ImageIO的接口,DownSampling,也就是生成縮略圖
     func downsample(imageAt imageURL: URL, to pointSize: CGSize, scale: CGFloat) -> UIImage
     {
         let sourceOpt = [kCGImageSourceShouldCache : false] as CFDictionary
         /**<
          這里有兩個(gè)注意事項(xiàng)
          
          設(shè)置kCGImageSourceShouldCache為false,避免緩存解碼后的數(shù)據(jù),64位設(shè)置上默認(rèn)是開(kāi)啟緩存的,(很好理解,因?yàn)橄麓问褂迷搱D片的時(shí)候,可能場(chǎng)景不同,需要生成的縮略圖大小是不同的,顯然不能做緩存處理)
          設(shè)置kCGImageSourceShouldCacheImmediately為true,避免在需要渲染的時(shí)候才做解碼,默認(rèn)選項(xiàng)是false
          */
         // 其他場(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)
     }
    
  2. 使用蘋(píng)果的CATiledLayer去加載。原理是分片渲染,滑動(dòng)時(shí)通過(guò)指定目標(biāo)位置,通過(guò)映射原圖指定位置的部分圖片數(shù)據(jù)解碼渲染。這里不再累述,有興趣的小伙伴可以自行了解下官方API。

五、網(wǎng)絡(luò)圖片加載方式:使用SDwebImage等三方庫(kù)

解決UIImageView的性能瓶頸

我們?cè)谟懻揢IImageView的性能瓶頸中發(fā)現(xiàn),問(wèn)題在于主線(xiàn)程進(jìn)行圖片解壓縮占用了大量的CPU,解決問(wèn)題的辦法就是:在子線(xiàn)程提前對(duì)圖片進(jìn)行強(qiáng)制解壓縮

而強(qiáng)制解壓縮的原理就是對(duì)圖片進(jìn)行重新繪制,得到一張新的解壓縮后的位圖。其中,用到的最核心的函數(shù)是 CGBitmapContextCreate :

/* Create a bitmap context. The context draws into a bitmap which is `width'
   pixels wide and `height' pixels high. The number of components for each
   pixel is specified by `space', which may also specify a destination color
   profile. The number of bits for each component of a pixel is specified by
   `bitsPerComponent'. The number of bytes per pixel is equal to
   `(bitsPerComponent * number of components + 7)/8'. Each row of the bitmap
   consists of `bytesPerRow' bytes, which must be at least `width * bytes
   per pixel' bytes; in addition, `bytesPerRow' must be an integer multiple
   of the number of bytes per pixel. `data', if non-NULL, points to a block
   of memory at least `bytesPerRow * height' bytes. If `data' is NULL, the
   data for context is allocated automatically and freed when the context is
   deallocated. `bitmapInfo' specifies whether the bitmap should contain an
   alpha channel and how it's to be generated, along with whether the
   components are floating-point or integer. */
CG_EXTERN CGContextRef __nullable CGBitmapContextCreate(void * __nullable data,
    size_t width, size_t height, size_t bitsPerComponent, size_t bytesPerRow,
    CGColorSpaceRef cg_nullable space, uint32_t bitmapInfo)
    CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);

如果 UIImage 中存儲(chǔ)的是已經(jīng)解碼后的數(shù)據(jù),速度就會(huì)快很多,所以?xún)?yōu)化的思路就是:在子線(xiàn)程中對(duì)圖片原始數(shù)據(jù)進(jìn)行強(qiáng)制解碼,再將解碼后的圖片拋回主線(xiàn)程繼續(xù)使用,從而提高主線(xiàn)程的響應(yīng)速度。
我們需要使用的工具是 Core Graphics 框架的 CGBitmapContextCreate 方法和相關(guān)的繪制函數(shù)??傮w的步驟是:

  1. 創(chuàng)建一個(gè)指定大小和格式的 bitmap context。
  2. 將未解碼圖片寫(xiě)入到這個(gè) context 中,這個(gè)過(guò)程包含了強(qiáng)制解碼。
  3. 從這個(gè) context 中創(chuàng)建新的 UIImage 對(duì)象,返回。

SDWebImage 實(shí)現(xiàn)

下面是SDWebImage的核心代碼:

// 1. 從 UIImage 對(duì)象中獲取 CGImageRef 的引用。這兩個(gè)結(jié)構(gòu)是蘋(píng)果在不同層級(jí)上對(duì)圖片的表示方式,UIImage 屬于 UIKit,是 UI 層級(jí)圖片的抽象,用于圖片的展示;CGImageRef 是 QuartzCore 中的一個(gè)結(jié)構(gòu)體指針,用C語(yǔ)言編寫(xiě),用來(lái)創(chuàng)建像素位圖,可以通過(guò)操作存儲(chǔ)的像素位來(lái)編輯圖片。這兩種結(jié)構(gòu)可以方便的互轉(zhuǎn):
CGImageRef imageRef = image.CGImage;

// 2. 調(diào)用 UIImage 的 +colorSpaceForImageRef: 方法來(lái)獲取原始圖片的顏色空間參數(shù)。
CGColorSpaceRef colorspaceRef = [UIImage colorSpaceForImageRef:imageRef];
        
size_t width = CGImageGetWidth(imageRef);
size_t height = CGImageGetHeight(imageRef);

// 3. 計(jì)算圖片解碼后每行需要的比特?cái)?shù),由兩個(gè)參數(shù)相乘得到:每行的像素?cái)?shù) width,和存儲(chǔ)一個(gè)像素需要的比特?cái)?shù)4(這里的4,其實(shí)是由每張圖片的像素格式和像素組合來(lái)決定的)
size_t bytesPerRow = 4 * width;

// 4. 最關(guān)鍵的函數(shù):調(diào)用 CGBitmapContextCreate() 方法,生成一個(gè)空白的圖片繪制上下文,我們傳入了上述的一些參數(shù),指定了圖片的大小、顏色空間、像素排列等等屬性。
CGContextRef context = CGBitmapContextCreate(NULL,
                                             width,
                                             height,
                                             kBitsPerComponent,
                                             bytesPerRow,
                                             colorspaceRef,
                                             kCGBitmapByteOrderDefault|kCGImageAlphaNoneSkipLast);
if (context == NULL) {
    return image;
}
        
// 5. 調(diào)用 CGContextDrawImage() 方法,將未解碼的 imageRef 指針內(nèi)容,寫(xiě)入到我們創(chuàng)建的上下文中,這個(gè)步驟,完成了隱式的解碼工作。
CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);

// 6. 從 context 上下文中創(chuàng)建一個(gè)新的 imageRef,這是解碼后的圖片了。
CGImageRef newImageRef = CGBitmapContextCreateImage(context);

// 7. 從 imageRef 生成供UI層使用的 UIImage 對(duì)象,同時(shí)指定圖片的 scale 和 orientation 兩個(gè)參數(shù)。
UIImage *newImage = [UIImage imageWithCGImage:newImageRef
                                        scale:image.scale
                                  orientation:image.imageOrientation];

CGContextRelease(context);
CGImageRelease(newImageRef);

return newImage;

通過(guò)以上的步驟,我們成功在子線(xiàn)程中對(duì)圖片進(jìn)行了強(qiáng)制轉(zhuǎn)碼,回調(diào)給主線(xiàn)程使用,從而大大提高了圖片的渲染效率。這也是現(xiàn)在主流 App 和大量三方庫(kù)的最佳實(shí)踐。

SDWebImage配置優(yōu)化,減小CG-raster-data內(nèi)存占用

在使用SDWebImage的時(shí)候,會(huì)默認(rèn)保存圖片解碼后的內(nèi)存,以便提高頁(yè)面的渲染速度,但是這會(huì)導(dǎo)致內(nèi)存的急速增加,所以可以在不影響體驗(yàn)的情況下,選擇機(jī)型和系統(tǒng),進(jìn)行優(yōu)化,避免大量的內(nèi)存占用,引起OOM問(wèn)題。關(guān)閉解碼內(nèi)存緩存的方法如下:

[[SDImageCache sharedImageCache] setShouldDecompressImages:NO];
[[SDWebImageDownloader sharedDownloader] setShouldDecompressImages:NO];

附錄

WWDC2018 圖像和圖形的最佳實(shí)踐
WWDC2018 深入iOS內(nèi)存
移動(dòng)端圖片格式調(diào)研
談?wù)?iOS 中圖片的解壓縮
iOS開(kāi)發(fā):圖片格式與性能優(yōu)化

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒(méi)有地址/指針的概念1.2> 泛型1.3> 類(lèi)型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,685評(píng)論 1 32
  • 誰(shuí)吃掉我們的CPU: 方法CA::Render::create_image_from_provider 圖片預(yù)解碼...
    神采飛揚(yáng)_2015閱讀 3,463評(píng)論 2 6
  • 要講圖片格式還先得從圖像的基本數(shù)據(jù)結(jié)構(gòu)說(shuō)起。在計(jì)算機(jī)中, 圖像是由一個(gè)個(gè)像素點(diǎn)組成,像素點(diǎn)就是顏色點(diǎn),而顏色最簡(jiǎn)單...
    404ErrorCrash閱讀 3,855評(píng)論 0 3
  • 卷首語(yǔ) 歡迎來(lái)到 objc.io 的第三期! 這一期都是關(guān)于視圖層的。當(dāng)然視圖層有很多方面,我們需要把它們縮小到幾...
    評(píng)評(píng)分分閱讀 1,944評(píng)論 0 18
  • 對(duì)于大多數(shù) iOS 應(yīng)用來(lái)說(shuō),圖片往往是最占用手機(jī)內(nèi)存的資源之一,同時(shí)也是不可或缺的組成部分。將一張圖片從磁盤(pán)中加...
    nongjiazhen閱讀 1,489評(píng)論 0 5

友情鏈接更多精彩內(nèi)容