iOS性能優(yōu)化(中級(jí))

欲知前事如何弟蚀,且看上回分解: iOS性能優(yōu)化(初級(jí))

小試牛刀

通過(guò)對(duì)性能初級(jí)優(yōu)化秘籍一段時(shí)間的練習(xí),少俠應(yīng)該對(duì)性能優(yōu)化有了一定的了解,在日常開(kāi)發(fā)編碼中有了些性能優(yōu)化的意識(shí)排苍,當(dāng)產(chǎn)品小師妹提出一個(gè)新的交互的時(shí)候枝笨,想必也定難不倒少俠了袁铐。

就列表來(lái)說(shuō)揭蜒,icon、大標(biāo)題剔桨、小標(biāo)題屉更、內(nèi)容,一般APP的很多時(shí)候就是這幾個(gè)元素洒缀,排版不同瑰谜,細(xì)致效果不同。這些對(duì)于少俠來(lái)說(shuō)都已經(jīng)不是問(wèn)題了树绩,無(wú)聲無(wú)息中APP已經(jīng)如絲般順滑萨脑。看著產(chǎn)品小師妹那敬仰的眼神饺饭,牛心潮澎湃渤早,花前月下,海誓山盟馬上就要脫口而出瘫俊。

折戟沉沙

但鹊杖,江湖風(fēng)云變幻,折戟沉沙你早有準(zhǔn)備扛芽。

只是沒(méi)想到那一天來(lái)的這么快骂蓖。

那一天產(chǎn)品小師妹提出了一個(gè)新的需求,除了之前的icon川尖、大標(biāo)題登下、小標(biāo)題之外,現(xiàn)在要加上標(biāo)簽空厌,標(biāo)簽有多個(gè)用于各種活動(dòng)運(yùn)營(yíng)庐船,標(biāo)簽的位置要根據(jù)標(biāo)題內(nèi)容的位置來(lái)定,標(biāo)簽要做成圓角加邊框嘲更,同時(shí)列表每一行的高度要根據(jù)各項(xiàng)內(nèi)容來(lái)最終確定筐钟,內(nèi)容多就高,內(nèi)容少就矮赋朦,還有icon要圓形加邊框順便帶點(diǎn)陰影篓冲,巴拉巴拉巴拉巴拉巴拉巴拉。
產(chǎn)品小師妹一口氣說(shuō)了很多宠哄,說(shuō)的你眼冒金星壹将,氣息紊亂,差點(diǎn)走火入魔毛嫉,口吐鮮血诽俯,但看著小師妹那一如既往的欣喜加期待的眼神,只好暗暗運(yùn)力承粤,穩(wěn)住陣腳暴区,一口答應(yīng)小師妹的需求闯团。
伊人遠(yuǎn)去,看著小師妹遠(yuǎn)去的身影仙粱,你瘋狂編碼房交,但總有那么一個(gè)點(diǎn)無(wú)法突破,流暢性始終無(wú)法達(dá)到要求伐割,不禁陷入了沉思候味。

臥薪嘗膽

初級(jí)性能優(yōu)化秘籍,只能應(yīng)對(duì)初級(jí)的性能優(yōu)化問(wèn)題隔心。但當(dāng)前的需求白群,效果多,子視圖多济炎,排版更新頻繁川抡,高度每行不一樣辐真。初級(jí)秘籍已經(jīng)不能很好的湊效须尚,這可如何是好。

少俠莫慌侍咱,老夫看你已經(jīng)熟練了初級(jí)性能優(yōu)化秘籍耐床,基礎(chǔ)已經(jīng)打牢,現(xiàn)在就將性能優(yōu)化中級(jí)秘籍傳授與你罷楔脯。

工欲善其事必先利其器撩轰,想要戰(zhàn)勝對(duì)手,你要有趁手的兵器昧廷。

在APP里直接的觀察看FPS數(shù)據(jù):

KMCGeigerCounter
也可以根據(jù) CADisplayLink 自己寫一個(gè)簡(jiǎn)易好用的堪嫂,CADisplayLink 是一個(gè)定時(shí)器,而且這個(gè)定時(shí)器的調(diào)用頻率跟屏幕刷新頻率相同木柬。

頂級(jí)法寶皆串,當(dāng)屬 Instrument:

若想熟練使用此項(xiàng)法寶,需注意兩個(gè)地方

  1. 用release模式眉枕,貼近最真是的使用環(huán)境恶复,才能獲得最準(zhǔn)確的數(shù)據(jù)。
  2. 用真機(jī)測(cè)試速挑,模擬器再厲害也還是在模擬谤牡,祭出不同型號(hào)的真機(jī),才能針對(duì)優(yōu)化姥宝。

打開(kāi)方式: Xcode -> Product -> Profile -> Core Animation 配合TimeProfile 一起使用
查看FPS的同時(shí)翅萤,還能查看到哪些操作比較耗時(shí),有此傍身腊满,再厲害的敵人也會(huì)露出破綻套么。

查看FPS

百步穿楊

性能優(yōu)化的步驟:
修改 -> Instrument查看 -> 修改 -> Instrument查看 —> 修改.....
重復(fù)以上動(dòng)作直到性能達(dá)到要求

CPU的耗時(shí)操作可以在Instrument里查看到流纹,并定位修改優(yōu)化,但GPU的優(yōu)化要怎么進(jìn)行呢违诗?

XCode9之后可以Xcode -> Debug > View Debugging > Rendering 下看到優(yōu)化的各個(gè)選項(xiàng)漱凝,模擬器時(shí)無(wú)法勾選,只有真機(jī)的情況下才能勾選诸迟。

查看優(yōu)化選項(xiàng)
  • Color Blended Layers — 出現(xiàn)圖層混合的地方會(huì)標(biāo)注為紅色茸炒,沒(méi)有圖層混合的地方會(huì)顯示為綠色,方向是紅色越少越好阵苇,綠色越多越好壁公。
圖層混合
  • Color Hits Green and Misses Red — 當(dāng)使用光柵化渲染(shouldRasterize)的時(shí)候,如果圖層是綠色绅项,表示這些緩存被復(fù)用紊册,如果圖層是紅色表示緩存沒(méi)有被復(fù)用會(huì)重復(fù)創(chuàng)建,這時(shí)候會(huì)造成性能問(wèn)題快耿。
光柵化
  • Color Copied Images — 如果GPU不支持當(dāng)前圖片格式囊陡,那么圖片會(huì)交給CPU進(jìn)行預(yù)先處理,這張圖片會(huì)顯示為藍(lán)色掀亥。

  • Color Misaligned Images — 檢測(cè)圖片是否被拉伸撞反,當(dāng)圖片色實(shí)際大小跟ImageView的大小不相同時(shí),就會(huì)發(fā)生搪花,顯示為黃色遏片,這種操作會(huì)比較消耗CPU資源。

  • Color Offscreen-rendered Yellow — GPU的渲染有兩種撮竿,On-screen Rendering當(dāng)前屏幕渲染吮便,是指GPU的渲染在當(dāng)前屏幕的緩沖區(qū)內(nèi)進(jìn)行。off-screen Rendering是指在GPU的渲染發(fā)生在當(dāng)前屏幕之外新開(kāi)辟的緩沖區(qū)幢踏。開(kāi)辟新的緩沖區(qū)髓需,切換緩沖區(qū)等會(huì)對(duì)性能有較大的影響。

觸發(fā)離屏渲染有以下幾種行為:

  1. cornerRadius以及masksToBounds同時(shí)使用時(shí)會(huì)觸發(fā)離屏渲染惑折,單獨(dú)使用時(shí)不會(huì)觸發(fā)授账。
  2. 設(shè)置shadow,而且shodowPath = nil時(shí)會(huì)觸發(fā)惨驶。
  3. mask 設(shè)置蒙版會(huì)觸發(fā)白热。
  4. layer.shouldRasterize的不適當(dāng)使用會(huì)觸發(fā)離屏渲染。
  5. layer.allowsGroupOpacity iOS7以后默認(rèn)開(kāi)啟粗卜;當(dāng)layer.opacity != 1.0且有subLayer或者背景圖時(shí)會(huì)觸發(fā)屋确。
  6. layer.allowsEdgeAntialiasing 在iOS8以后的系統(tǒng)里可能已經(jīng)做了優(yōu)化,并不會(huì)觸發(fā)離屏渲染,不會(huì)對(duì)性能造成影響攻臀。
  7. 重寫了drawRect焕数。

少俠熟讀了以上招式,便能快速找出對(duì)手的破綻刨啸。

無(wú)堅(jiān)不摧

找出了敵人的破綻堡赔,少俠還要制定詳細(xì)的應(yīng)對(duì)策略,瞅準(zhǔn)時(shí)機(jī)设联,方能一招制敵善已。

老夫這就給你展示制敵之道:

  • Color Blended Layers:

    • UIView的backgroundColor不要設(shè)置為clearColor,最好設(shè)置的和superView的backgroundColor顏色一樣离例。
    • 圖片避免使用帶alpha通道的圖片换团,無(wú)論是本地圖片還是后臺(tái)返回圖片。什么宫蛆,設(shè)計(jì)妹子不同意艘包,少俠這就要靠你的魅力啦。
  • Color Hits Green and Misses Red: 在初級(jí)性能優(yōu)化中耀盗,適當(dāng)使用shouldRasterize中有詳細(xì)講解想虎。

  • Color Copied Images: 開(kāi)發(fā)過(guò)程中注意圖片格式

  • Color Misaligned Images: 盡量把圖片大小設(shè)置的和UIImageView相同大小。

  • Color Offscreen-rendered Yellow: 這是性能優(yōu)化的要點(diǎn)袍冷,針對(duì)引起離屏渲染的各種情況需要逐一應(yīng)對(duì)

重點(diǎn)來(lái)了磷醋,離屏渲染的優(yōu)化招式,少俠看仔細(xì)了

  • 設(shè)置圓角cornerRadius:

UIView: 如果view.layer.contents 為空胡诗,直接通過(guò)設(shè)置view.layer.cornerRadius 以及 view.backgroundColor或者view.layer.border即可設(shè)置圓角,不需要設(shè)置masksToBounds為YES淌友,此時(shí)不會(huì)產(chǎn)生離屏渲染煌恢。

//設(shè)置圓角邊框
view.layer.cornerRadius = 3.0
view.layer.borderColor =  UIColor.red.cgColor
view.layer.borderWidth = 1.0
// 設(shè)置帶背景
view.layer.cornerRadius = 3.0
view.backgroundColor = UIColor.green
//或者相同效果的
view.layer.backgroundColor = UIColor.green.cgColor

UILabel: 設(shè)置和UIView差不多,有一個(gè)區(qū)別就是設(shè)置label.backgroundColor和layer.cornerRadius不會(huì)起效果震庭,需要設(shè)置label.layer.backgroundColor和layer.cornerRadius才會(huì)起效果瑰抵。

//設(shè)置圓角邊框
view.layer.cornerRadius = 3.0
view.layer.borderColor =  UIColor.red.cgColor
view.layer.borderWidth = 1.0

UITextField: 自帶圓角效果,設(shè)置不同style即可達(dá)到效果器联。

UITextView: 和UIView的設(shè)置方法相同二汛。

UIImageView: UIImageView的情況比較特殊,上面的幾種方法不能實(shí)現(xiàn)圓角拨拓,必須要layer.cornerRadius和layer.masksToBounds = YES肴颊,才能實(shí)現(xiàn)圓角。但這個(gè)操作必定會(huì)產(chǎn)生離屏渲染渣磷,為了避免離屏渲染婿着,常用的優(yōu)化方法有:

  • 重繪圖片,生成一張帶圓角的圖片,然后設(shè)置到UIImageView上竟宋。

    func redrawImage(originImage: UIImage, rectSize: CGSize, cornerRadius: CGFloat) -> UIImage? {
       UIGraphicsBeginImageContextWithOptions(rectSize, false, UIScreen.main.scale)
       if let context = UIGraphicsGetCurrentContext() {
             let rect = CGRect(origin: CGPoint.zero, size: rectSize)
             let path = UIBezierPath(roundedRect: rect, byRoundingCorners: .allCorners, cornerRadii: CGSize(width: cornerRadius, height: cornerRadius))
             context.addPath(path.cgPath)
             context.clip()
             originImage.draw(in: rect)
             context.drawPath(using: .fillStroke)
             let roundedImage = UIGraphicsGetImageFromCurrentImageContext()
             UIGraphicsEndImageContext()
             return roundedImage
            }
        return nil
     }
     
    DispatchQueue.global(qos: .default).async {
             //在子線程調(diào)用redrawImage生成圖片
             DispatchQueue.main.async {
                 //在主線程設(shè)置圖片
             }
         }
    
  • 在UIImageView上遮蓋一張部分透明的提完,部分遮擋的圖片,蓋在原來(lái)的UIImageView上丘侠,曲線實(shí)現(xiàn)圖片圓角功能徒欣。

    //生成中間透明 周圍遮擋的圖片
    func getRundedCornerImage(radius: CGFloat, rectSize: CGSize, fillColor: UIColor) -> UIImage? {
      UIGraphicsBeginImageContextWithOptions(rectSize, false, UIScreen.main.scale)
      if let currentContext = UIGraphicsGetCurrentContext() {
        let rect = CGRect(origin: .zero, size: rectSize)
        let outerPath = UIBezierPath(rect: rect)
        let innerPath = UIBezierPath(roundedRect: rect,
                                   byRoundingCorners: .allCorners,
                                   cornerRadii: CGSize(width: radius, height: radius))
        currentContext.setBlendMode(.normal)
        fillColor.setFill()
        outerPath.fill()
      
        currentContext.setBlendMode(.normal)
        innerPath.fill()
        let roundedCornerImage = UIGraphicsGetImageFromCurrentImageContext()
      UIGraphicsEndImageContext()
        return roundedCornerImage
        }
      return nil
    }
      
    //將生成的圖片 加到需要圓角的圖片的上方
    
  • 設(shè)置陰影shadow:

    設(shè)置shadowPath,可以解決離屏渲染問(wèn)題蜗字。

     self.shadowView.layer.shadowColor = UIColor.gray.cgColor
     self.shadowView.layer.shadowOpacity = 0.2
     self.shadowView.layer.shadowRadius = 3.0
     self.shadowView.layer.shadowOffset = CGSize(width: 1, height: 1)
     self.shadowView.layer.shadowPath = UIBezierPath(rect: view.bounds).cgPath
    

    當(dāng)然和圓角的解決辦法一樣帚称,可以使用一張帶陰影的圖來(lái)曲線解決問(wèn)題。

    func getRundedCornerShadowImage(originImage: UIImage, rectSize: CGSize, roundedRadius: CGFloat, shadowColor: UIColor, shadowOffset: CGSize, insetX: CGFloat, insetY: CGFloat) -> UIImage? {
     UIGraphicsBeginImageContextWithOptions(rectSize, false, UIScreen.main.scale)
     if let currentContext = UIGraphicsGetCurrentContext() {
         let rect = CGRect(origin: .zero, size: rectSize)
         let shadowPath = UIBezierPath(roundedRect: rect.insetBy(dx: insetX, dy: insetY),
                                 byRoundingCorners: .allCorners,
                                 cornerRadii: CGSize(width: roundedRadius, height: roundedRadius))
         currentContext.setShadow(offset: shadowOffset, blur: roundedRadius, color: shadowColor.cgColor)
         currentContext.addPath(shadowPath.cgPath)
         shadowPath.fill()
    
         let imagePath = UIBezierPath(roundedRect: rect.insetBy(dx: insetX, dy: insetY),
                                       byRoundingCorners: .allCorners,
                                       cornerRadii: CGSize(width: roundedRadius, height: roundedRadius))
         currentContext.addPath(imagePath.cgPath)
         currentContext.clip()
         originImage.draw(in: rect.insetBy(dx: insetX, dy: insetY))
         currentContext.strokePath()
         
         let image = UIGraphicsGetImageFromCurrentImageContext()
         UIGraphicsEndImageContext()
         return image
     }
     return nil
    }  
    
  • 設(shè)置蒙版mask:

    設(shè)置mask必定會(huì)觸發(fā)離屏渲染秽澳。
    mask的過(guò)程大致來(lái)看是和視圖混合相反的過(guò)程闯睹,例如有一張圖片,中間有一個(gè)圓形空間是透明的担神,邊緣部分是白色楼吃,如果視圖直接疊加在一張頭像上,會(huì)呈現(xiàn)出圓形頭型的效果妄讯,但如果使用mask則會(huì)顯示出中間白邊緣透明的效果孩锡。
    所以性能敏感的界面中,可以不使用mask亥贸,而使用視圖混合這種對(duì)性能影響更小的方式進(jìn)行操作躬窜。

  • layer.allowsGroupOpacity、layer.allowsEdgeAntialiasing:

    這兩個(gè)操作對(duì)性能并不會(huì)造成比較大的影響炕置。

  • drawRect:

    drawRect會(huì)造成較大的內(nèi)存消耗荣挨,并會(huì)造成離屏渲染,應(yīng)盡量避免重寫朴摊。

爐火純青

以上招式默垄,少俠可看好了,日后定當(dāng)好好練習(xí)甚纲,獲得伊人芳心指日可待口锭。

快去找小師妹去罷。

欲知后事如何介杆,且看下回分解: iOS性能優(yōu)化(中級(jí)+): 異步繪制

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末鹃操,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子春哨,更是在濱河造成了極大的恐慌荆隘,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,723評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件悲靴,死亡現(xiàn)場(chǎng)離奇詭異臭胜,居然都是意外死亡莫其,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門耸三,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)乱陡,“玉大人,你說(shuō)我怎么就攤上這事仪壮『┑撸” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 152,998評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵积锅,是天一觀的道長(zhǎng)爽彤。 經(jīng)常有香客問(wèn)我,道長(zhǎng)缚陷,這世上最難降的妖魔是什么适篙? 我笑而不...
    開(kāi)封第一講書人閱讀 55,323評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮箫爷,結(jié)果婚禮上嚷节,老公的妹妹穿的比我還像新娘。我一直安慰自己虎锚,他們只是感情好硫痰,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著窜护,像睡著了一般效斑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上柱徙,一...
    開(kāi)封第一講書人閱讀 49,079評(píng)論 1 285
  • 那天缓屠,我揣著相機(jī)與錄音,去河邊找鬼坐搔。 笑死藏研,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的概行。 我是一名探鬼主播,決...
    沈念sama閱讀 38,389評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼弧岳,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼凳忙!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起禽炬,我...
    開(kāi)封第一講書人閱讀 37,019評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤涧卵,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后腹尖,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體柳恐,經(jīng)...
    沈念sama閱讀 43,519評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了乐设。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片讼庇。...
    茶點(diǎn)故事閱讀 38,100評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖近尚,靈堂內(nèi)的尸體忽然破棺而出蠕啄,到底是詐尸還是另有隱情,我是刑警寧澤戈锻,帶...
    沈念sama閱讀 33,738評(píng)論 4 324
  • 正文 年R本政府宣布歼跟,位于F島的核電站,受9級(jí)特大地震影響格遭,放射性物質(zhì)發(fā)生泄漏哈街。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評(píng)論 3 307
  • 文/蒙蒙 一拒迅、第九天 我趴在偏房一處隱蔽的房頂上張望骚秦。 院中可真熱鬧,春花似錦坪它、人聲如沸骤竹。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,289評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蒙揣。三九已至,卻和暖如春开瞭,著一層夾襖步出監(jiān)牢的瞬間懒震,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,517評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工嗤详, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留个扰,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,547評(píng)論 2 354
  • 正文 我出身青樓葱色,卻偏偏與公主長(zhǎng)得像递宅,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子苍狰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評(píng)論 2 345

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