簡單-UIKit性能分析優(yōu)化

本文是基于UIKit性能調(diào)優(yōu)實(shí)戰(zhàn)講解 - 簡書這篇文章的總結(jié)詳細(xì)請看原文

實(shí)例講解可以參考:小心別讓圓角成了你列表的幀數(shù)殺手 - CocoaChina_讓移動開發(fā)更簡單

fps表示frames per second岁经,也就是每秒鐘顯示多少幀畫面。對于靜止不變的內(nèi)容冷尉,我們不需要考慮它的刷新率兜挨,但在執(zhí)行動畫或滑動時褐桌,fps的值直接反映出滑動的流暢程度

調(diào)試優(yōu)化:

1.圖層混合:

首先理解像素:像素就是屏幕上的每一個點(diǎn)由RGB三種顏色構(gòu)成有時候會帶有alpha勘究,如果某一個區(qū)域覆蓋了多個layer聘裁,最后的顯示效果就會收到影響艺挪,顯色混合會消耗一定的GPU赴叹,當(dāng)把最上層的顏色透明度設(shè)置為100%時GPU就會忽略下面所有的layer節(jié)省不必要的運(yùn)算

第一個調(diào)試選項(xiàng)"Color Blended Layers"正是用于檢測哪里發(fā)生了圖層混合鸿染,并用紅色標(biāo)記出來。因此我們需要盡可能減少看到的紅色區(qū)域乞巧。一旦發(fā)現(xiàn)應(yīng)該想法設(shè)法消除它

解決方案:a.將控件的opaque = true 原理是希望避免圖層混合涨椒,但是作用不大,因?yàn)閁IView的這個屬性默認(rèn)就是true只要不是人為的設(shè)置為透明都不會出現(xiàn)圖層混合绽媒,但對于UIImageView他本身和圖片都不能為透明的蚕冬,圖片自身的性質(zhì)也可能會對結(jié)果產(chǎn)生影響,比opaque屬性更重要的是backgroundColor屬性是辕,如果不設(shè)置這個屬性囤热,控件依然被認(rèn)為是透明的,所以我們做的是將他設(shè)置為白色

PS:如果label文字有中文获三,依然會出現(xiàn)圖層混合旁蔼,這是因?yàn)榇藭rlabel多了一個sublayer,如果有好的解決辦法歡迎告訴我疙教。

2.光柵化光柵化會導(dǎo)致離屏渲染

光柵化是將一個layer預(yù)先渲染成位圖(bitmap)棺聊,然后加入緩存中。如果對于陰影效果這樣比較消耗資源的靜態(tài)內(nèi)容進(jìn)行緩存贞谓,可以得到一定幅度的性能提升限佩。demo中的這一行代碼表示將label的layer光柵化:

label.layer.shouldRasterize?=?true

Instrument中,第二個調(diào)試選項(xiàng)是“Color Hits Green and Misses Red”裸弦,它表示如果命中緩存則顯示為綠色祟同,否則顯示為紅色,顯然綠色越多越好理疙,紅色越少越好晕城。

光柵化的緩存機(jī)制是一把雙刃劍,先寫入緩存再讀取有可能消耗較多的時間窖贤。因此光柵化僅適用于較復(fù)雜的广辰、靜態(tài)的效果暇矫。通過Instrument的調(diào)試發(fā)現(xiàn)主之,這里使用光柵化經(jīng)常出現(xiàn)未命中緩存的情況择吊,如果沒有特殊需要則可以關(guān)閉光柵化

3.顏色格式:

像素在內(nèi)存中的布局和它在磁盤中的存儲方式并不相同〔坜龋考慮一種簡單的情況:每個像素有R几睛、G、B和alpha四個值粤攒,每個值占用1字節(jié)所森,因此每個像素占用4字節(jié)的內(nèi)存空間。一張1920*1080的照片(iPhone6 Plus的分辨率)一共有2,073,600個像素夯接,因此占用了超過8Mb的內(nèi)存焕济。但是一張同樣分辨率的PNG格式或JPEG格式的圖片一般情況下不會有這么大。這是因?yàn)镴PEG將像素?cái)?shù)據(jù)進(jìn)行了一種非常復(fù)雜且可逆的轉(zhuǎn)化盔几。

比如應(yīng)用中有一些從網(wǎng)絡(luò)下載的圖片晴弃,而GPU恰好不支持這個格式,這就需要CPU預(yù)先進(jìn)行格式轉(zhuǎn)化

第三個選項(xiàng)“Color Copied Images”就用來檢測這種實(shí)時的格式轉(zhuǎn)化逊拍,如果有則會將圖片標(biāo)記為藍(lán)色上鞠。

4.圖片大小

第五個選項(xiàng)“Color Misaligned Images”。它表示如果圖片需要縮放則標(biāo)記為黃色芯丧,如果沒有像素對齊則標(biāo)記為紫色

第三個優(yōu)化是調(diào)整所有圖片的像素大小以避免不必要的縮放芍阎。

5.離屏渲染

正常的渲染通道:OpenGL提交一個命令到Command Buffer,隨后GPU開始渲染缨恒,渲染結(jié)果放到Render Buffer中谴咸,這是正常的渲染流程

有一些復(fù)雜的效果無法直接渲染出結(jié)果,它需要分步渲染最后再組合起來骗露,比如添加一個蒙版(mask):GPU分別得到了紋理(texture岭佳,也就是那個相機(jī)圖標(biāo))和layer(藍(lán)色的蒙版)的渲染結(jié)果。但這兩個渲染結(jié)果沒有直接放入Render Buffer中椒袍,也就表示這是離屏渲染驼唱。直到第三個渲染通道,才把兩者組合起來放入Render Buffer中驹暑。離屏渲染意味著把渲染結(jié)果臨時保存玫恳,等用到時再取出,因此相對于普通渲染更占用資源优俘。

第六個選項(xiàng)“Color Offscreen-Rendered Yellow”會把需要離屏渲染的地方標(biāo)記為黃色京办,大部分情況下我們需要盡可能避免黃色的出現(xiàn)。離屏渲染可能會自動觸發(fā)帆焕,也可以手動觸發(fā)惭婿。以下情況可能會導(dǎo)致觸發(fā)離屏渲染:

重寫drawRect方法

有mask或者是陰影(layer.masksToBounds, layer.shadow*)不恭,模糊效果也是一種mask

layer.shouldRasterize = true

前兩者會自動觸發(fā)離屏渲染,第三種方法是手動開啟離屏渲染财饥。

第四個優(yōu)化换吧,在設(shè)置陰影效果的四行代碼下面添加一行:

imgView.layer.shadowPath = UIBezierPath(rect: imgView.bounds).CGPath

這行代碼制定了陰影路徑,如果沒有手動指定钥星,Core Animation會去自動計(jì)算沾瓦,這就會觸發(fā)離屏渲染。如果人為指定了陰影路徑谦炒,就可以免去計(jì)算贯莺,從而避免產(chǎn)生離屏渲染。

設(shè)置cornerRadius本身并不會導(dǎo)致離屏渲染宁改,但很多時候它還需要配合layer.masksToBounds = true使用缕探。根據(jù)之前的總結(jié),設(shè)置masksToBounds會導(dǎo)致離屏渲染还蹲。解決方案是盡可能在滑動時避免設(shè)置圓角爹耗,如果必須設(shè)置圓角,可以使用光柵化技術(shù)將圓角緩存起來:

//?設(shè)置圓角 ? ?ps:用后用后無效果

label.layer.masksToBounds?=?true

label.layer.cornerRadius?=?8

label.layer.shouldRasterize?=?true

label.layer.rasterizationScale?=?layer.contentsScale

6.快速路徑:

之前將離屏渲染和渲染路徑時的示意圖么秽誊,離屏渲染的最后一步是把此前的多個路徑組合起來鲸沮。如果這個組合過程能由CPU完成,就會大量減少GPU的工作锅论。這種技術(shù)在繪制地圖中可能用到讼溺。

第七個選項(xiàng)“Color Compositing Fast-Path Blue”用于標(biāo)記由硬件繪制的路徑,藍(lán)色越多越好最易。

7.變化區(qū)域

刷新視圖時怒坯,我們應(yīng)該把需要重繪的區(qū)域盡可能縮小。對于未發(fā)生變化的內(nèi)容則不應(yīng)該重繪藻懒,第八個選項(xiàng)“Flash updated Regions”用于標(biāo)記發(fā)生重繪的區(qū)域剔猿。一個典型的例子是系統(tǒng)的時鐘應(yīng)用,絕大多數(shù)時候只有顯示秒針的區(qū)域需要重繪:

總結(jié)

如果你一步一步做到了這里嬉荆,我想一定會有不少收益归敬。不過,學(xué)而不思則罔鄙早,思而不學(xué)則殆汪茧。動手實(shí)踐后還是應(yīng)該總結(jié)提煉,優(yōu)化滑動性能主要涉及三個方面:

避免圖層混合

確毕薹控件的opaque屬性設(shè)置為true舱污,確保backgroundColor和父視圖顏色一致且不透明

如無特殊需要,不要設(shè)置低于1的alpha值

確保UIImage沒有alpha通道

避免臨時轉(zhuǎn)換

確保圖片大小和frame一致弥虐,不要在滑動時縮放圖片

確保圖片顏色格式被GPU支持扩灯,避免勞煩CPU轉(zhuǎn)換

慎用離屏渲染

絕大多數(shù)時候離屏渲染會影響性能

重寫drawRect方法媚赖,設(shè)置圓角、陰影珠插、模糊效果惧磺,光柵化都會導(dǎo)致離屏渲染

設(shè)置陰影效果是加上陰影路徑

滑動時若需要圓角效果,開啟光柵化

實(shí)戰(zhàn)

本文的demo可以在我的Github上下載丧失,然后一步一步自己體驗(yàn)優(yōu)化過程豺妓。但demo畢竟是刻意搭建的一個環(huán)境,我會在我自己的仿寫的簡書app上不斷進(jìn)行實(shí)戰(zhàn)優(yōu)化布讹,歡迎共同學(xué)習(xí)交流。

代碼:

UIView圓角:

#import "UIView+AddCorner.h"

@implementation UIView (AddCorner)

-(double)roundbyuint:(double) num anUint:(double )unit{

double remain = modf(num, &unit);

if (remain > unit / 2.0) {

return [self ceilbyuint:num andUint:unit];

} else {

return [self floorbyuint:num andUint:unit];

}

}

-(double)ceilbyuint:(double)num andUint:(double)unit{

return num - modf(num, &unit) + unit;

//將浮點(diǎn)數(shù)num分解成整數(shù)部分和小數(shù)部分

}

-(double)floorbyuint:(double)num andUint:(double)unit{

return num - modf(num, &unit) ;

}

-(double)piexl:(double)num{

double unit;

switch ((int)[UIScreen mainScreen].scale) {

case 1: unit = 1.0/1.0; break;

case 2: unit = 1.0/2.0; break;

case 3: unit = 1.0/3.0; break;

default: unit = 0.0;

break;

}

return [self roundbyuint:num anUint:unit];

}

-(void)kt_viewAddCorner:(CGFloat)radius andBorderWidth:(CGFloat)borderWidth andBorderColor:(UIColor*)borderColor andBackgroundColor:(UIColor*)backgroundColor{

UIImageView * imageView = [[UIImageView alloc]initWithImage:[self ViewAddCorner:radius andBorderWidth:borderWidth andBorderColor:borderColor andBackgroundColor:backgroundColor]] ;

[self insertSubview:imageView atIndex:0];

}

-(UIImage *)ViewAddCorner:(CGFloat)radius andBorderWidth:(CGFloat)borderWidth andBorderColor:(UIColor*)borderColor andBackgroundColor:(UIColor*)backgroundColor {

CGSize sizeToFit = CGSizeMake((double)CGRectGetWidth(self.frame), (double)CGRectGetHeight(self.frame));

CGFloat halfBorderWidth = (CGFloat)borderWidth / 2.0;

UIGraphicsBeginImageContextWithOptions(sizeToFit, false, [UIScreen mainScreen].scale);

CGContextRef context = UIGraphicsGetCurrentContext();

CGContextSetLineWidth(context, borderWidth);

CGContextSetStrokeColorWithColor(context, borderColor.CGColor);

CGContextSetFillColorWithColor(context, backgroundColor.CGColor);

CGFloat width = sizeToFit.width, height = sizeToFit.height;

CGContextMoveToPoint(context, width - halfBorderWidth, radius + halfBorderWidth);? // 開始坐標(biāo)右邊開始

CGContextAddArcToPoint(context, width - halfBorderWidth, height - halfBorderWidth, width - radius - halfBorderWidth, height - halfBorderWidth, radius);? // 右下角角度

CGContextAddArcToPoint(context, halfBorderWidth, height - halfBorderWidth, halfBorderWidth, height - radius - halfBorderWidth, radius); // 左下角角度

CGContextAddArcToPoint(context, halfBorderWidth, halfBorderWidth, width - halfBorderWidth, halfBorderWidth, radius); // 左上角

CGContextAddArcToPoint(context, width - halfBorderWidth, halfBorderWidth, width - halfBorderWidth, radius + halfBorderWidth, radius) ;// 右上角

CGContextDrawPath(UIGraphicsGetCurrentContext(), kCGPathFillStroke);

UIImage? *output = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

return output;

}

@end

UIImageView圓角:

#import "UIImageView+AddCorner.h"

@implementation UIImageView (AddCorner)

-(void)kt_ImageViewAddCornerWithRadius:(CGFloat)radius{

// self.image = self.image?:[self.image imageAddCornerWithRadius:radius andSize:self.bounds.size];

//注意第三個選項(xiàng)的設(shè)置

UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, [UIScreen mainScreen].scale);

//在繪制之前先裁剪出一個圓形

[[UIBezierPath bezierPathWithRoundedRect:self.bounds

cornerRadius:radius] addClip];

//圖片在設(shè)置的圓形里面進(jìn)行繪制

[self.image drawInRect:self.bounds];

//獲取圖片

self.image = UIGraphicsGetImageFromCurrentImageContext();

//結(jié)束繪制

UIGraphicsEndImageContext();

}

@end

UIImage圓角:

#import "UIImage+ImageRoundedCorner.h"

@implementation UIImage (ImageRoundedCorner)

- (UIImage*)imageAddCornerWithRadius:(CGFloat)radius andSize:(CGSize)size{

CGRect rect = CGRectMake(0, 0, size.width, size.height);

UIGraphicsBeginImageContextWithOptions(size, NO, [UIScreen mainScreen].scale);

CGContextRef ctx = UIGraphicsGetCurrentContext();

UIBezierPath * path = [UIBezierPath bezierPathWithRoundedRect:rect byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(radius, radius)];

CGContextAddPath(ctx,path.CGPath);

CGContextClip(ctx);

[self drawInRect:rect];

CGContextDrawPath(ctx, kCGPathFillStroke);

UIImage * newImage = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

return newImage;

}

@end

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末训堆,一起剝皮案震驚了整個濱河市描验,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌坑鱼,老刑警劉巖膘流,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異鲁沥,居然都是意外死亡呼股,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門画恰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來彭谁,“玉大人,你說我怎么就攤上這事允扇〔郑” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵考润,是天一觀的道長狭园。 經(jīng)常有香客問我,道長糊治,這世上最難降的妖魔是什么唱矛? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮井辜,結(jié)果婚禮上绎谦,老公的妹妹穿的比我還像新娘。我一直安慰自己抑胎,他們只是感情好燥滑,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著阿逃,像睡著了一般铭拧。 火紅的嫁衣襯著肌膚如雪赃蛛。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天搀菩,我揣著相機(jī)與錄音呕臂,去河邊找鬼。 笑死肪跋,一個胖子當(dāng)著我的面吹牛歧蒋,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播州既,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼谜洽,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了吴叶?” 一聲冷哼從身側(cè)響起阐虚,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蚌卤,沒想到半個月后实束,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡逊彭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年咸灿,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片侮叮。...
    茶點(diǎn)故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡避矢,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出签赃,到底是詐尸還是另有隱情谷异,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布锦聊,位于F島的核電站歹嘹,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏孔庭。R本人自食惡果不足惜尺上,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望圆到。 院中可真熱鬧怎抛,春花似錦、人聲如沸芽淡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽挣菲。三九已至富稻,卻和暖如春掷邦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背椭赋。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工抚岗, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人哪怔。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓宣蔚,卻偏偏與公主長得像,于是被迫代替她去往敵國和親认境。 傳聞我的和親對象是個殘疾皇子胚委,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評論 2 345

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

  • 實(shí)際開發(fā)中,大多會遇到圓角或者圓形的控件的情況元暴。通常篷扩,簡便的解決方案主要是: 1.讓美工做一個圓角的圖片...
    LanWor閱讀 1,641評論 1 5
  • Quartz2D以及drawRect的重繪機(jī)制字?jǐn)?shù)1487 閱讀21 評論1 喜歡1一、什么是Quartz2D Q...
    PurpleWind閱讀 760評論 0 3
  • 圓角是一種常見UI效果設(shè)計(jì)茉盏,與直角相比要柔和許多,適合暖色調(diào)設(shè)計(jì)枢冤。而在設(shè)置圓角時會帶來一定的性能損耗鸠姨,下面介紹幾種...
    簡魚7819閱讀 2,655評論 0 10
  • //設(shè)置尺寸為屏幕尺寸的時候self.window = [[UIWindow alloc] initWithFra...
    LuckTime閱讀 793評論 0 0
  • 做前端的小伙伴無論在學(xué)習(xí)還是工作中呀潭,應(yīng)該還是或多或少會遇到點(diǎn)IE兼容問題谤职,當(dāng)然我們巴不得IE的份額越來越低..( ̄...
    Aleph_Zheng閱讀 162評論 0 0