中文字幕在线观看,亚洲а∨天堂久久精品9966,亚洲成a人片在线观看你懂的,亚洲av成人片无码网站,亚洲国产精品无码久久久五月天

Swift-圖像的性能優(yōu)化

2018-07-20    來源:編程學(xué)習(xí)網(wǎng)

容器云強(qiáng)勢上線!快速搭建集群,上萬Linux鏡像隨意使用

前言

隨著移動(dòng)端的發(fā)展,現(xiàn)在越來越注重性能優(yōu)化了。這篇文章將談一談對(duì)于圖片的性能優(yōu)化。面試中又會(huì)經(jīng)常有這樣的問題:如何實(shí)現(xiàn)一個(gè)圖像的圓角,不要用 cornerRadius ?

模擬器常用性能測試工具

Color Blended Layers(混合圖層->檢測圖像的混合模式)

  • 此功能基于渲染程度對(duì)屏幕中的混合區(qū)域進(jìn)行 綠->紅 的高亮(也就是多個(gè)半透明層的疊加,其中綠色代表比較好,紅色則代表比較糟糕)
  • 由于重繪的原因,混合對(duì) GPU ( Graphics Processing Unit->專門用來畫圖的 )性能會(huì)有影響,同時(shí)也是滑動(dòng)或者動(dòng)畫幀率下降的罪魁禍?zhǔn)字?/li>

GPU:如果有透明的圖片疊加,做兩個(gè)圖像透明度之間疊加的運(yùn)算,運(yùn)算之后生成一個(gè)結(jié)果,顯示到屏幕上,如果透明的圖片疊加的很多,運(yùn)算量就會(huì)很大

png 格式的圖片是透明的,如果邊上有無色的地方,那么可以把底下的背景透過來

一般指定顏色的時(shí)候不建議使用透明色,透明色執(zhí)行效率低

Color Copied Images(圖像復(fù)制->幾乎用不到)

  • 有時(shí)候 寄宿圖片(layer.content) 的生成是由 Core Animation 被強(qiáng)制生成一些圖片,然后發(fā)送到渲染服務(wù)器,而不是簡單的指向原始指針
  • 這個(gè)選項(xiàng)把這些圖片渲染成藍(lán)色
  • 復(fù)制圖片對(duì)內(nèi)存和 CPU 使用來說都是一項(xiàng)非常昂貴的操作,所以應(yīng)該盡可能的避免

Color Misaligned Images(拉伸圖像->檢測圖片有沒有被拉伸)

  • 會(huì)高亮那些被縮放或者拉伸以及沒有正確對(duì)齊到像素邊界的圖片(也就是非整型坐標(biāo))
  • 通常都會(huì)導(dǎo)致圖片的不正?s放,比如把一張大圖當(dāng)縮略圖顯示,或者不正確的模糊圖像

如果圖片做 拉伸 的動(dòng)作,是消耗 CPU 的。如果圖片顯示在一個(gè) Cell 上面,滾出屏幕再滾動(dòng)回來的時(shí)候,圖片仍然需要重新被設(shè)置,在進(jìn)入屏幕之前還需要一次 拉伸操作 ,這些 拉伸 的操作是會(huì)消耗 CPU 的計(jì)算的。這樣的設(shè)置多了以后就會(huì)嚴(yán)重影響性能。一個(gè)圖片是否被進(jìn)行了 拉伸操作 ,我們用模擬器就可以判斷出來。

為什么我們說這種方法設(shè)置圖像效果不好

Color Misaligned Images(拉伸圖像->檢測圖片有沒有被拉伸)

創(chuàng)建一個(gè)自定義尺寸的 ImageView ,并設(shè)置圖像

let image = UIImage(named: "avatar_default")

let imageView01 = UIImageView(frame: CGRect(x: 100, y: 100, width: 160, height: 160))
imageView01.image = image
view.addSubview(imageView01)

圖片在模擬器上的顯示

利用模擬器的 Debug 的 Color Misaligned Images 功能查看圖片狀態(tài)。如下圖所示,圖片顯示黃色,證明圖片被拉伸了。

就知道你可能會(huì)不相信,繼續(xù)看!將 ImageView 的尺寸設(shè)置成和圖片一樣大小,再利用模擬器 Color Misaligned Images 功能再次查看圖片狀態(tài)。結(jié)果如圖所示

事實(shí)證明,如果圖像尺寸和 ImageView 尺寸不一致,圖像就一定會(huì)被拉伸,只要被拉伸, CPU 就會(huì)工作,如果是在 cell 上,每次 cell 離開屏幕再回到屏幕的時(shí)候,都會(huì)對(duì)圖片進(jìn)行拉伸處理。就會(huì)頻繁的消耗 CPU 從而導(dǎo)致影響 APP 的性能。

Color Offscreen-Rendered(離屏渲染->有待完善)

  • 這里會(huì)把那些需要離屏渲染的圖層高亮成黃色
  • 這些圖層很可能需要用 shadownPath 或者 shouldRasterize(柵格化) 來優(yōu)化

好處:圖像提前生成

壞處: CPUGPU 會(huì)頻繁的切換,會(huì)導(dǎo)致 CPU 的消耗會(huì)高一點(diǎn),但是性能會(huì)提升

小結(jié):

  • 以上性能優(yōu)化中,有效的檢測 Color Blended Layers 和 Color Misaligned Images 在開發(fā)中能夠提升圖像的性能
  • Color Copied Images 幾乎遇不到
  • Color Offscreen-Rendered 主要用于 cell 的性能優(yōu)化

解決圖片拉伸問題

利用核心繪圖功能實(shí)現(xiàn),根據(jù)尺寸獲取路徑,重新繪制一個(gè)目標(biāo)尺寸的圖片

override func viewDidLoad() {
    super.viewDidLoad()

    let image = UIImage(named: "avatar_default")

    let imageView01 = UIImageView(frame: CGRect(x: 100, y: 100, width: 160, height: 160))
    imageView01.image = image
    view.addSubview(imageView01)

    let rect = CGRect(x: 100, y: 300, width: 160, height: 160)
    let imageView02 = UIImageView(frame: rect)

    // 自定義創(chuàng)建圖像的方法
    imageView02.image = avatarImage(image: image!, size: rect.size)
    view.addSubview(imageView02)

}

自定義創(chuàng)建圖像的方法

/// 將給定的圖像進(jìn)行拉伸,并且返回新的圖像
///
/// - Parameters:
///   - image: 原圖
///   - size: 目標(biāo)尺寸
/// - Returns: 返回一個(gè)新的'目標(biāo)尺寸'的圖像
func avatarImage(image: UIImage, size: CGSize) -> UIImage? {

    let rect = CGRect(origin: CGPoint(), size: size)

    // 1.圖像的上下文-內(nèi)存中開辟一個(gè)地址,跟屏幕無關(guān)
    /**
     * 1.繪圖的尺寸
     * 2.不透明:false(透明) / true(不透明)
     * 3.scale:屏幕分辨率,默認(rèn)情況下生成的圖像使用'1.0'的分辨率,圖像質(zhì)量不好
     *         可以指定'0',會(huì)選擇當(dāng)前設(shè)備的屏幕分辨率
     */
    UIGraphicsBeginImageContextWithOptions(rect.size, false, 0)

    // 2.繪圖'drawInRect'就是在指定區(qū)域內(nèi)拉伸屏幕
    image.draw(in: rect)

    // 3.取得結(jié)果
    let result = UIGraphicsGetImageFromCurrentImageContext()

    // 4.關(guān)閉上下文
    UIGraphicsEndImageContext()

    // 5.返回結(jié)果
    return result
}

效果如下

如果到這里你以為就完事了,那你真是太年輕了

再解決混合模式 (Color Blended Layers) 問題

繼續(xù)剛才的話題,僅僅解決了拉伸問題后,在 Color Blended Layers(混合模式) 下還是有問題,如圖

將繪圖選項(xiàng)的透明狀態(tài)設(shè)置為 不透明(true)

到這里,如果類似新聞 APP 圖片都只是顯示方形的,就可以搞定了。那如果是頭像怎么辦呢?頭像絕大多數(shù)都是圓角頭像,而且現(xiàn)在越來越多的考慮到性能方面的問題。很多人都不用 cornerRadius ,認(rèn)為用 cornerRadius 不是一個(gè)好的解決辦法。

設(shè)置圖像圓角,不用 cornerRadius

在 獲取上下文(UIGraphicsBeginImageContextWithOptions) 和 繪圖(drawInRect) 之間實(shí)例化一個(gè)圓形的路徑,并進(jìn)行路徑裁切

// 1> 實(shí)例化一個(gè)圓形的路徑
let path = UIBezierPath(ovalIn: rect)
// 2> 進(jìn)行路徑裁切 - 后續(xù)的繪圖,都會(huì)出現(xiàn)在圓形路徑內(nèi)部,外部的全部干掉
path.addClip()

效果如下

UIGraphicsBeginImageContextWithOptions(rect.size, true, 0) 這里選擇了 true(不透明) ,四個(gè)角即使被裁切掉(沒有在獲取到的路徑里面)但是由于是 不透明 的模式,所以看不到下面的顏色,默認(rèn)看到了黑色的背景。

將 UIGraphicsBeginImageContextWithOptions(rect.size, true, 0) 透明模式改為 false(透明)

再看下混合模式,四個(gè)叫和頭像都是紅色,并且顏色深淺程度不一樣,越紅效率越不好。證明有圖層疊加的運(yùn)算,因此,不能采用透明的模式。

解決辦法:給背景設(shè)置一個(gè)顏色,使其不顯示默認(rèn)的黑色。

這樣就可以解決四個(gè)角顯示黑色的問題,并且在混合模式狀態(tài)下不會(huì)再有紅色顯示,性能可以非常的好。

開發(fā)過程中,用顏色比用圖片性能會(huì)高一點(diǎn)。

不到萬不得已, View 的背景色盡量不要設(shè)置成透明顏色。

給圖像添加邊框,繪制內(nèi)切的圓形

UIColor.darkGray.setStroke()
path.lineWidth = 5      // 默認(rèn)是'1'
path.stroke()

判斷一個(gè)應(yīng)用程序的好壞,看圖像處理的是否到位,如果表格里面圖像都拉伸,并且設(shè)置 cornerRadius ,那么表格的卡頓可能將會(huì)變得非常明顯。

下面是方法的最終代碼:

/// 將給定的圖像進(jìn)行拉伸,并且返回新的圖像
///
/// - Parameters:
///   - image: 原圖
///   - size: 目標(biāo)尺寸
/// - Returns: 返回一個(gè)新的'目標(biāo)尺寸'的圖像
func avatarImage(image: UIImage, size: CGSize, backColor:UIColor?) -> UIImage? {

    let rect = CGRect(origin: CGPoint(), size: size)

    // 1.圖像的上下文-內(nèi)存中開辟一個(gè)地址,跟屏幕無關(guān)
    /**
     * 1.繪圖的尺寸
     * 2.不透明:false(透明) / true(不透明)
     * 3.scale:屏幕分辨率,默認(rèn)情況下生成的圖像使用'1.0'的分辨率,圖像質(zhì)量不好
     *         可以指定'0',會(huì)選擇當(dāng)前設(shè)備的屏幕分辨率
     */
    UIGraphicsBeginImageContextWithOptions(rect.size, true, 0)

    // 背景填充(在裁切之前做填充)
    backColor?.setFill()
    UIRectFill(rect)

    // 1> 實(shí)例化一個(gè)圓形的路徑
    let path = UIBezierPath(ovalIn: rect)
    // 2> 進(jìn)行路徑裁切 - 后續(xù)的繪圖,都會(huì)出現(xiàn)在圓形路徑內(nèi)部,外部的全部干掉
    path.addClip()

    // 2.繪圖'drawInRect'就是在指定區(qū)域內(nèi)拉伸屏幕
    image.draw(in: rect)

    // 3.繪制內(nèi)切的圓形
    UIColor.darkGray.setStroke()
    path.lineWidth = 5      // 默認(rèn)是'1'
    path.stroke()

    // 4.取得結(jié)果
    let result = UIGraphicsGetImageFromCurrentImageContext()

    // 5.關(guān)閉上下文
    UIGraphicsEndImageContext()

    // 6.返回結(jié)果
    return result
}

封裝

為了方便自己以后用,因此,將其封裝起來。如果有更好的改進(jìn)辦法歡迎給我提出。

建立了一個(gè)空白文件 HQImage ,在 UIImage 的 extension 里面自定義了兩個(gè)方法 創(chuàng)建頭像圖像(hq_avatarImage) 和 創(chuàng)建矩形圖像(hq_rectImage)

// MARK: - 創(chuàng)建圖像的自定義方法
extension UIImage {

    /// 創(chuàng)建圓角圖像
    ///
    /// - Parameters:
    ///   - size: 尺寸
    ///   - backColor: 背景色(默認(rèn)`white`)
    ///   - lineColor: 線的顏色(默認(rèn)`lightGray`)
    /// - Returns: 裁切后的圖像
    func hq_avatarImage(size: CGSize?, backColor: UIColor = UIColor.white, lineColor: UIColor = UIColor.lightGray) -> UIImage? {

        var size = size

        if size == nil {
            size = self.size
        }

        let rect = CGRect(origin: CGPoint(), size: size!)

        // 1.圖像的上下文-內(nèi)存中開辟一個(gè)地址,跟屏幕無關(guān)
        /**
         * 1.繪圖的尺寸
         * 2.不透明:false(透明) / true(不透明)
         * 3.scale:屏幕分辨率,默認(rèn)情況下生成的圖像使用'1.0'的分辨率,圖像質(zhì)量不好
         *         可以指定'0',會(huì)選擇當(dāng)前設(shè)備的屏幕分辨率
         */
        UIGraphicsBeginImageContextWithOptions(rect.size, true, 0)

        // 背景填充(在裁切之前做填充)
        backColor.setFill()
        UIRectFill(rect)

        // 1> 實(shí)例化一個(gè)圓形的路徑
        let path = UIBezierPath(ovalIn: rect)
        // 2> 進(jìn)行路徑裁切 - 后續(xù)的繪圖,都會(huì)出現(xiàn)在圓形路徑內(nèi)部,外部的全部干掉
        path.addClip()

        // 2.繪圖'drawInRect'就是在指定區(qū)域內(nèi)拉伸屏幕
        draw(in: rect)

        // 3.繪制內(nèi)切的圓形
        UIColor.darkGray.setStroke()
        path.lineWidth = 1      // 默認(rèn)是'1'
        path.stroke()

        // 4.取得結(jié)果
        let result = UIGraphicsGetImageFromCurrentImageContext()

        // 5.關(guān)閉上下文
        UIGraphicsEndImageContext()

        // 6.返回結(jié)果
        return result
    }

    /// 創(chuàng)建矩形圖像
    ///
    /// - Parameters:
    ///   - size: 尺寸
    ///   - backColor: 背景色(默認(rèn)`white`)
    ///   - lineColor: 線的顏色(默認(rèn)`lightGray`)
    /// - Returns: 裁切后的圖像
    func hq_rectImage(size: CGSize?, backColor: UIColor = UIColor.white, lineColor: UIColor = UIColor.lightGray) -> UIImage? {

        var size = size

        if size == nil {
            size = self.size
        }

        let rect = CGRect(origin: CGPoint(), size: size!)

        // 1.圖像的上下文-內(nèi)存中開辟一個(gè)地址,跟屏幕無關(guān)
        /**
         * 1.繪圖的尺寸
         * 2.不透明:false(透明) / true(不透明)
         * 3.scale:屏幕分辨率,默認(rèn)情況下生成的圖像使用'1.0'的分辨率,圖像質(zhì)量不好
         *         可以指定'0',會(huì)選擇當(dāng)前設(shè)備的屏幕分辨率
         */
        UIGraphicsBeginImageContextWithOptions(rect.size, true, 0)

        // 2.繪圖'drawInRect'就是在指定區(qū)域內(nèi)拉伸屏幕
        draw(in: rect)

        // 3.取得結(jié)果
        let result = UIGraphicsGetImageFromCurrentImageContext()

        // 4.關(guān)閉上下文
        UIGraphicsEndImageContext()

        // 5.返回結(jié)果
        return result
    }
}

性能測試

沒有對(duì)比就無從談起性能優(yōu)化,以下是我根據(jù)兩種方法,循環(huán)創(chuàng)建 100 個(gè) ImageView 的 CPU內(nèi)存 消耗(個(gè)人感覺 1 張圖片不一定能說明問題,所以搞了 100 個(gè))

系統(tǒng)方法創(chuàng)建圖像

for _ in 0..<100 {

    let imageView01 = UIImageView(frame: CGRect(x: 100, y: 100, width: 160, height: 160))
    imageView01.image = image
    view.addSubview(imageView01)
}

自定義方法創(chuàng)建圖像

for _ in 0..<100 {

    let rect02 = CGRect(x: 100, y: 300, width: 160, height: 160)
    let imageView02 = UIImageView(frame: rect02)
    imageView02.image = avatarImage(image: image!, size: rect02.size, backColor: view.backgroundColor)
    view.addSubview(imageView02)
}

由此可見,新方法對(duì)CPU消耗明顯減少,內(nèi)存較以前稍微上漲,CPU消耗減少,則性能有所提升。(因?yàn)槊看蜗牟皇且粋(gè)定數(shù),我這里也是測了很多次取的大概的平均值。)

 

來自:https://juejin.im/post/59a3e67df265da2473444281

 

標(biāo)簽: 代碼 服務(wù)器

版權(quán)申明:本站文章部分自網(wǎng)絡(luò),如有侵權(quán),請(qǐng)聯(lián)系:west999com@outlook.com
特別注意:本站所有轉(zhuǎn)載文章言論不代表本站觀點(diǎn)!
本站所提供的圖片等素材,版權(quán)歸原作者所有,如需使用,請(qǐng)與原作者聯(lián)系。

上一篇:iOS開發(fā):為你的應(yīng)用兼容iPhone X

下一篇:OkHttp3 架構(gòu)分析