[TOC]
緩存栋猖!緩存净薛!還是緩存!
- 緩存是啥蒲拉?cache肃拜、buffer。也許你沒用過雌团,但是你一定見過無數(shù)次這兩個(gè)單詞燃领。
- 緩存有啥用?復(fù)用锦援、效率提升猛蔽,典型的空間換時(shí)間。
這篇文章講的內(nèi)容就是從緩存開始的,下面來看看一些常見的緩存曼库。
class : cache_t
在方法查找階段:
- 先從類的緩存中去取
- 若沒有找到区岗,再從類的方法列表中找
- 找到后,會(huì)將方法存到緩存中(方便下一次的讀取毁枯,提高查找效率)
http 緩存
- cookies:用戶信息慈缔,http中解決無法定位用戶身份問題
- body:數(shù)據(jù)緩存,避免每次都要從后臺(tái)讀取种玛,使得數(shù)據(jù)的獲取更快藐鹤,效率更高,減少流量的浪費(fèi)赂韵,降低服務(wù)器的負(fù)載
SDWebImage :圖片緩存
- 內(nèi)存緩存
- 磁盤緩存
NSStream : buffer
這個(gè)例子和其他的稍微有點(diǎn)區(qū)別娱节,應(yīng)該叫緩沖區(qū),其實(shí)也是一個(gè)緩存祭示,但它的使用場景更加偏向于本章重點(diǎn)——離屏渲染
NSStream:Input -> Output
建立通道之后括堤,數(shù)據(jù)流不是直接從 Input 輸入到 Output,而是先輸入到一個(gè) data buffer 里面绍移,然后 Output 從 data buffer 里面取
NSStream:Input -> Buffer -> Output
這樣有什么好處呢悄窃?
- 當(dāng)接收端下行網(wǎng)絡(luò)環(huán)境較差時(shí),我們可以將更多的數(shù)據(jù)存到這個(gè) buffer 里面蹂窖,等到它網(wǎng)絡(luò)恢復(fù)的時(shí)候再讀取轧抗,不會(huì)造成數(shù)據(jù)的丟失
- 同樣,當(dāng)發(fā)送端上行網(wǎng)絡(luò)差時(shí)瞬测,接收端可以從 buffer 中取數(shù)據(jù)横媚,降低網(wǎng)絡(luò)對數(shù)據(jù)傳輸?shù)挠绊?/li>
UITableView : 緩存行高
tableview的問題對于iOS開發(fā)者來說是老生常談的問題了,其中有一條就是盡量避免使用 estimatedHeightForRowAtIndexPath
來設(shè)置高度月趟,對于動(dòng)態(tài)高度灯蝴,我們一般會(huì)提前計(jì)算好高度,緩存起來孝宗,然后通過 heightForRowAtIndexPath
來設(shè)置高度穷躁。
除了上面提到的一些常見的緩存,我們在實(shí)際開發(fā)中還有更多的自定義的緩存策略因妇,比如組件化開發(fā)中问潭,對組件的緩存。
render buffer:渲染緩存(幀緩存)
片元著色器給片元上色之后的像素怎么處理呢婚被?直接顯示到屏幕上嗎狡忙?
并不是,而是存在一個(gè)渲染緩存(幀緩存)里面址芯,等到下一次runloop到來時(shí)灾茁,從幀緩存中讀取數(shù)據(jù)窜觉,然后顯示到屏幕上。
下圖展示了蘋果的雙緩存技術(shù)北专,當(dāng)只有一個(gè)緩存時(shí)禀挫,會(huì)出現(xiàn)掉幀等不良現(xiàn)象,所以蘋果給了兩個(gè)緩存區(qū)來存儲(chǔ)數(shù)據(jù)逗余。
offscreen buffer:離屏緩存
上面說到了特咆,蘋果都給了兩個(gè)緩存區(qū)來存儲(chǔ)渲染數(shù)據(jù)季惩,那為啥還會(huì)有離屏緩存呢录粱?
當(dāng)需要繪制的圖像由多個(gè)圖層組成時(shí),便需要將前面的渲染數(shù)據(jù)存起來画拾,然后再對這些數(shù)據(jù)進(jìn)行混合啥繁,得到新的數(shù)據(jù),顯示到屏幕上青抛。離屏的操作便發(fā)生在存數(shù)據(jù)的時(shí)候旗闽。(幀緩存里面的渲染數(shù)據(jù),使用完就會(huì)被丟棄蜜另,不會(huì)保存)
- 案例1:mask 渲染流程
- 渲染 layer mask 紋理适室,存儲(chǔ)到離屏緩存區(qū)里面
- 渲染 layer content 紋理,存儲(chǔ)到離屏緩存區(qū)里面
- 混合上面的紋理举瑰,存儲(chǔ)到幀緩存區(qū)捣辆,等待顯示
- 案例2:UIBlurEffect 渲染流程
1-4:渲染內(nèi)容、捕獲內(nèi)容此迅、水平模糊汽畴、垂直模糊,這些結(jié)果都是存儲(chǔ)在離屏緩存區(qū)里面耸序;
5:合成上面的結(jié)果忍些,存儲(chǔ)到幀緩存區(qū),等待下一次顯示坎怪。
離屏渲染的理解
關(guān)于離屏渲染以及渲染過程罢坝,可以查看02總結(jié)--005--OpenGL 渲染全解析[轉(zhuǎn)載]這篇文章,這里主要是補(bǔ)充一些對離屏渲染的理解搅窿。比如前面講了離屏渲染其實(shí)就是一個(gè)緩存區(qū)炸客,這是一個(gè)很重要的理解思路。
離屏渲染流程
- 正常流程:將內(nèi)容渲染完成之后戈钢,不停地放入 Framebuffer 中痹仙,然后顯示屏幕不斷地從 Framebuffer 中讀取內(nèi)容,顯示實(shí)時(shí)內(nèi)容殉了;
- 離屏渲染:創(chuàng)建 Offscreenbuffer >> 將提前渲染好的內(nèi)容放入其中 >> 等到合適的時(shí)機(jī)在將 OffScreenbuffer 中的內(nèi)容進(jìn)一步疊加开仰、渲染 >> 最后將結(jié)果放入 Framebuffer 中;
- 顯示屏幕最后獲取數(shù)據(jù)的來源都是 幀緩存Framebuffer;
- Offscreenbuffer 只是一個(gè)臨時(shí)存儲(chǔ)渲染數(shù)據(jù)的地方
離屏渲染原理
- Layer的層級
- backgroundColor
- contents
- borderWidth / borderColor
如上圖所示众弓,layer 由三層組成恩溅,我們設(shè)置圓角通常會(huì)首先像下面這行代碼一樣進(jìn)行設(shè)置:
view.layer.cornerRadius = 2
- 設(shè)置
layer.cornerRadius
只會(huì)設(shè)置 backgroundColor 和 border 的圓角,不會(huì)設(shè)置 contents - 同時(shí)設(shè)置
layer.masksToBounds
才會(huì)設(shè)置 contents 的圓角谓娃。(對應(yīng)view中的clipsToBounds屬性)
下面通過幾個(gè)案例來理解這張圖片
- 按鈕存在背景圖片
- 同時(shí)設(shè)置了
cornerRadius
和clipsToBounds
這兩個(gè)屬性脚乡,會(huì)對 layer 的三層都起作用; - button中設(shè)置了圖片
setImage
滨达,說明它的contents中存在內(nèi)容奶稠,需要渲染的內(nèi)容有;(這里相當(dāng)于給按鈕添加了一個(gè)imageview) - 對于復(fù)合圖形的渲染捡遍,是需要借助
Offscreenbuffer
緩存的锌订,所以觸發(fā)了離屏渲染
- 同時(shí)設(shè)置了
//1.按鈕存在背景圖片
UIButton *btn1 = [UIButton buttonWithType:UIButtonTypeCustom];
btn1.frame = CGRectMake(100, 30, 100, 100);
btn1.layer.cornerRadius = 50;
[self.view addSubview:btn1];
[btn1 setImage:[UIImage imageNamed:@"btn.png"] forState:UIControlStateNormal];
btn1.clipsToBounds = YES;
既然會(huì)造成離屏渲染,那如果直接對 imageview 設(shè)置圓角呢画株?結(jié)果是不會(huì)觸發(fā)離屏渲染
btn1.imageView.layer.cornerRadius = 50;
btn1.imageView.layer.masksToBounds = YES;
- 按鈕不存在背景圖片
- 同時(shí)設(shè)置了
cornerRadius
和clipsToBounds
這兩個(gè)屬性辆飘,會(huì)對 layer 的三層都起作用 - 而且還設(shè)置了
backgroundColor
, - 但是 btn2 中并沒有 contents 的元素谓传,只有本身的
backgroundColor
蜈项,是一個(gè)單一圖層,所以不會(huì)觸發(fā)離屏渲染
- 同時(shí)設(shè)置了
//2.按鈕不存在背景圖片
UIButton *btn2 = [UIButton buttonWithType:UIButtonTypeCustom];
btn2.frame = CGRectMake(100, 180, 100, 100);
btn2.layer.cornerRadius = 50;
btn2.backgroundColor = [UIColor blueColor];
[self.view addSubview:btn2];
btn2.clipsToBounds = YES;
- UIImageView 設(shè)置了圖片+背景色;
- backgroundColor:[UIColor blueColor];
- contents: [UIImage imageNamed:@"btn.png"];
- 所以會(huì)離屏渲染
//3.UIImageView 設(shè)置了圖片+背景色;
UIImageView *img1 = [[UIImageView alloc]init];
img1.frame = CGRectMake(100, 320, 100, 100);
img1.backgroundColor = [UIColor blueColor];
[self.view addSubview:img1];
img1.layer.cornerRadius = 50;
img1.layer.masksToBounds = YES;
img1.image = [UIImage imageNamed:@"btn.png"];
- UIImageView 只設(shè)置了圖片,無背景色;
- 只有 contents: [UIImage imageNamed:@"btn.png"];
- 所以不會(huì)造成離屏渲染
//4.UIImageView 只設(shè)置了圖片,無背景色;
UIImageView *img2 = [[UIImageView alloc]init];
img2.frame = CGRectMake(100, 480, 100, 100);
[self.view addSubview:img2];
img2.layer.cornerRadius = 50;
img2.layer.masksToBounds = YES;
img2.image = [UIImage imageNamed:@"btn.png"];
【總結(jié)】
通過上面的這幾個(gè)例子來說续挟,我們不能直接根據(jù) 圓角和裁剪 來判斷是否觸發(fā)離屏渲染紧卒,我們應(yīng)該根據(jù)離屏渲染的原理來說明。
離屏渲染優(yōu)劣勢
-
優(yōu)勢:使用離屏渲染的原因
- 一些特殊效果需要使用額外的 Offscreen Buffer 來保存渲染的中間狀態(tài)庸推,所以不得不使用離屏渲染常侦。
- 出于效率目的,可以將內(nèi)容提前渲染保存在 Offscreen Buffer 中贬媒,達(dá)到復(fù)用的目的聋亡。
-
劣勢:避免離屏渲染的原因
- 離屏渲染時(shí)由于 App 需要提前對部分內(nèi)容進(jìn)行額外的渲染并保存到 Offscreen Buffer,以及需要在必要時(shí)刻對 Offscreen Buffer 和 Framebuffer 進(jìn)行內(nèi)容切換际乘,所以會(huì)需要更長的處理時(shí)間(實(shí)際上這兩步關(guān)于 buffer 的切換代價(jià)都非常大)
- 并且 Offscreen Buffer 本身就需要額外的空間坡倔,大量的離屏渲染可能早能內(nèi)存的過大壓力。與此同時(shí)脖含,Offscreen Buffer 的總大小也有限罪塔,不能超過屏幕總像素的 2.5 倍。
- 可見離屏渲染的開銷非常大养葵,一旦需要離屏渲染的內(nèi)容過多征堪,很容易造成掉幀的問題。所以大部分情況下关拒,我們都應(yīng)該盡量避免離屏渲染佃蚜。
離屏渲染具體邏輯
1. 畫家算法
剛才說了圓角加上 masksToBounds 的時(shí)候庸娱,因?yàn)?masksToBounds 會(huì)對 layer 上的所有內(nèi)容進(jìn)行裁剪,從而誘發(fā)了離屏渲染谐算,那么這個(gè)過程具體是怎么回事呢熟尉,下面我們來仔細(xì)講一下。
圖層的疊加繪制大概遵循“畫家算法”洲脂,在這種算法下會(huì)按層繪制斤儿,首先繪制距離較遠(yuǎn)的場景,然后用繪制距離較近的場景覆蓋較遠(yuǎn)的部分恐锦。
在普通的 layer 繪制中往果,上層的 sublayer 會(huì)覆蓋下層的 sublayer,下層 sublayer 繪制完之后就可以拋棄了踩蔚,從而節(jié)約空間提高效率棚放。所有 sublayer 依次繪制完畢之后枚粘,整個(gè)繪制過程完成馅闽,就可以進(jìn)行后續(xù)的呈現(xiàn)了。假設(shè)我們需要繪制一個(gè)三層的 sublayer馍迄,不設(shè)置裁剪和圓角福也,那么整個(gè)繪制過程就如下圖所示:
[圖片上傳失敗...(image-cde829-1594132539692)]
而當(dāng)我們設(shè)置了 cornerRadius 以及 masksToBounds 進(jìn)行圓角 + 裁剪時(shí),如前文所述攀圈,masksToBounds 裁剪屬性會(huì)應(yīng)用到所有的 sublayer 上暴凑。這也就意味著所有的 sublayer 必須要重新被應(yīng)用一次圓角+裁剪,這也就意味著所有的 sublayer 在第一次被繪制完之后赘来,并不能立刻被丟棄现喳,而必須要被保存在 Offscreen buffer 中等待下一輪圓角+裁剪,這也就誘發(fā)了離屏渲染犬辰,具體過程如下:
實(shí)際上不只是圓角+裁剪嗦篱,如果設(shè)置了透明度+組透明(layer.allowsGroupOpacity+layer.opacity
),陰影屬性(shadowOffset
等)都會(huì)產(chǎn)生類似的效果幌缝,因?yàn)榻M透明度灸促、陰影都是和裁剪類似的,會(huì)作用與 layer 以及其所有 sublayer 上涵卵,這就導(dǎo)致必然會(huì)引起離屏渲染浴栽。
離屏渲染案例
- 渲染mask,存入離屏緩存區(qū)轿偎;
- 渲染layer典鸡,存入離屏緩存區(qū);
- 讀取離屏緩存區(qū)的數(shù)據(jù)坏晦,然后進(jìn)行混合操作萝玷,將結(jié)果存入幀緩存區(qū)伊者;
- 等待下一次 runloop到來,顯示到屏幕上间护;
- Content:渲染內(nèi)容
- capture content:捕獲內(nèi)容
- Horizontal Blur:水平模糊
- Vertical Blur:垂直模糊
- Compositing Pass:合并過程
- 合并完成之后將結(jié)果存入幀緩存區(qū)亦渗,等待下一次 runloop到來,顯示到屏幕上
光柵化在離屏渲染中扮演的角色
shouldRasterize 光柵化的使用建議:
- 如果 layer 不能被復(fù)用汁尺,則沒有必要打開光柵化法精;
- 如果 layer 不是靜態(tài)的,需要被頻繁修改痴突,比如處于動(dòng)畫之中搂蜓,那么開啟了離屏渲染反而會(huì)影響效率;
- 離屏渲染緩存內(nèi)容有時(shí)間限制辽装,緩存內(nèi)容 100ms 內(nèi)如果沒有被使用帮碰,那么它就會(huì)被丟棄調(diào),無法進(jìn)行復(fù)用拾积;
- 離屏渲染緩存內(nèi)容有空間限制殉挽,超過 2.5 倍屏幕像素大小的話,也會(huì)失效拓巧,且無法進(jìn)行復(fù)用斯碌;
這里的 shouldRasterize 光柵化的使用建議
也是我們使用離屏渲染的建議。
光柵化和離屏渲染的聯(lián)系
從上面離屏渲染的原理中可以知道肛度,如果我們只是單一的圖層顯示傻唾,是不會(huì)觸發(fā)離屏渲染的,而當(dāng)我們開啟光柵化之后承耿,不管是單一圖層還是復(fù)合圖層冠骄,都會(huì)觸發(fā)離屏渲染。
所以光柵化的目的就是強(qiáng)制開啟離屏渲染加袋。
- 最后一行打開了光柵化凛辣,所以也開啟了離屏渲染
(離屏)緩存的時(shí)效性
上面說的離屏渲染其實(shí)就是一個(gè)緩存,我們知道緩存一般時(shí)效性很低锁荔,對于Offscreenbuffer中存儲(chǔ)的數(shù)據(jù)的緩存時(shí)間是 100ms
常見圓角觸發(fā)的情況以及處理辦法
常見圓角觸發(fā)的情況
- 使用了 mask 的 layer(layer.mask)
- 需要進(jìn)行裁剪的 layer(layer.masksToBounds / view.clipsToBounds)
- 設(shè)置了組透明度為 YES蟀给,并且透明度不為1 的layer(layer.allosGroupOpacity / layer.opacity)
- 添加了投影的 layer(layer.shadow*)
- 繪制了文字的 layer(UILabel,CATextLayer阳堕,Core Text等)
圓角的處理辦法
-
方案一【按鈕上的圖片】:使用 btn1.imageView
上面的案例中也提到了跋理,直接對 imageview 設(shè)置圓角,不要對button設(shè)置圓角
btn1.imageView.layer.cornerRadius = 50; btn1.imageView.layer.masksToBounds = YES;
-
方案二:創(chuàng)建一個(gè)圓角圖片
@implementation UIImage (CornerRadius) - (UIImage *)roundedCornerImageWithCornerRadius:(CGFloat)cornerRadius { CGFloat w = self.size.width; CGFloat h = self.size.height; CGFloat scale = UIScreen.mainScreen.scale; // 防止圓角半徑小于0, 或者大于寬/高中較小值的一半 cornerRadius = MAX(cornerRadius, 0); cornerRadius = MIN(cornerRadius, MIN(w, h)/2); UIImage* image = nil; CGRect imageFrame = CGRectMake(0, 0, w, h); UIGraphicsBeginImageContextWithOptions(self.size, NO, scale); UIBezierPath* path = [UIBezierPath bezierPathWithRoundedRect:imageFrame cornerRadius:cornerRadius]; [path addClip]; [self drawInRect:imageFrame]; image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return image; } @end
-
方案三:設(shè)置Imageview的image
@implementation UIImageView (MaskBounds) - (void)addMaskToBounds:(CGRect)maskBounds WithCornerRadius:(CGFloat)cornerRadius { CGFloat w = maskBounds.size.width; CGFloat h = maskBounds.size.height; CGFloat scale = UIScreen.mainScreen.scale; CGSize size = maskBounds.size; CGRect imageRect = CGRectMake(0, 0, w, h); // 防止圓角半徑小于0, 或者大于寬/高中較小值的一半 cornerRadius = MAX(cornerRadius, 0); cornerRadius = MIN(cornerRadius, MIN(w, h)/2); UIImage* image = self.image; UIGraphicsBeginImageContextWithOptions(size, NO, scale); UIBezierPath* path = [UIBezierPath bezierPathWithRoundedRect:imageRect cornerRadius:cornerRadius]; [path addClip]; [image drawInRect:imageRect]; self.image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); } @end
自測也是總結(jié)
以下內(nèi)容都是筆者的理解恬总,歡迎留言給出其他解釋前普。
-
CPU 和 GPU 的設(shè)計(jì)目的分別是什么?
- CPU 處理邏輯壹堰、控制核心拭卿、依賴高
- GPU 處理大量簡單的計(jì)算骡湖、依賴低
-
CPU 和 GPU 哪個(gè)的 Cache\ALU\Control unit 的比例更高?
- CPU 中緩存和控制單元比例高
- GPU 中計(jì)算單元比例高
-
計(jì)算機(jī)圖像渲染流水線的大致流程是什么峻厚?
- Application:處理事件响蕴、提交動(dòng)畫(界面可能會(huì)發(fā)生變化);
- CoreAnimation:CPU處理顯示內(nèi)容的前置計(jì)算惠桃,例如布局計(jì)算浦夷、解碼等任務(wù),然后將圖層打包傳遞到下一層(渲染層)辜王;
- Render server:GPU渲染流程劈狐。(
過程:頂點(diǎn)著色器->光柵化->片元著色器->存到幀緩存區(qū),結(jié)果:原始圖元->新圖元->片元->像素->位圖
) - 等待下一個(gè)runloop的到來呐馆,將位圖顯示到屏幕上
-
Framebuffer 幀緩沖器的作用是什么肥缔?
- 存儲(chǔ)GPU渲染結(jié)果(位圖),等待下一個(gè)runloop的到來汹来,顯示到屏幕上
-
Screen Tearing 屏幕撕裂是怎么造成的续膳?
- 電子束在掃描新的一幀時(shí),位圖還沒有處理好
- 掃描到中間的時(shí)候俗慈,位圖處理好了
- 這時(shí)姑宽,上半部是上一幀的畫面遣耍,下半部是這一幀的畫面闺阱,所以造成撕裂
-
如何解決屏幕撕裂的問題?
- 垂直同步(Vsync)+雙緩存區(qū)(Double Buffering)
-
掉幀是怎么產(chǎn)生的舵变?
- 由于同步的問題酣溃,幀緩存區(qū)中畫面的顯示是按順序顯示的
- 當(dāng)CPU+GPU在16.67ms內(nèi)沒有完成一幀的計(jì)算時(shí)
- 下一次runloop的到來,并不能從幀緩存區(qū)中拿到新圖像
- 所以顯示的還是上一次的畫面纪隙,所以造成掉幀
-
CoreAnimation 的職責(zé)是什么赊豌?
- 主要職責(zé)包含:渲染、構(gòu)建和實(shí)現(xiàn)動(dòng)畫绵咱。
- 是 app 界面渲染和構(gòu)建的最基礎(chǔ)架構(gòu)
- 盡可能快地組合屏幕上不同的可視內(nèi)容碘饼,并且被存儲(chǔ)為樹狀層級結(jié)構(gòu)
- 這個(gè)樹也形成了 UIKit 以及在 iOS 應(yīng)用程序當(dāng)中你所能在屏幕上看見的一切的基礎(chǔ)
-
UIView 和 CALayer 是什么關(guān)系?有什么區(qū)別悲伶?
- 相同的層級結(jié)構(gòu):我們對 UIView 的層級結(jié)構(gòu)非常熟悉艾恼,由于每個(gè) UIView 都對應(yīng) CALayer 負(fù)責(zé)頁面的繪制,所以 CALayer 也具有相應(yīng)的層級結(jié)構(gòu)麸锉。
- 部分效果的設(shè)置:因?yàn)?UIView 只對 CALayer 的部分功能進(jìn)行了封裝钠绍,而另一部分如圓角、陰影花沉、邊框等特效都需要通過調(diào)用 layer 屬性來設(shè)置柳爽。
- 是否響應(yīng)點(diǎn)擊事件:CALayer 不負(fù)責(zé)點(diǎn)擊事件媳握,所以不響應(yīng)點(diǎn)擊事件,而 UIView 會(huì)響應(yīng)磷脯。
- 不同繼承關(guān)系:CALayer 繼承自 NSObject蛾找,UIView 由于要負(fù)責(zé)交互事件,所以繼承自 UIResponder赵誓。
-
為什么會(huì)同時(shí)有 UIView 和 CALayer腋粥,能否合成一個(gè)?
- 單一職責(zé)原則架曹,UIView 和 CALayer 分別負(fù)責(zé)自己獨(dú)立的職責(zé)
- CALayer的復(fù)用隘冲,CALayer除了服務(wù)于UIKit之外,還服務(wù)于AppKit绑雄,在mac開發(fā)中也會(huì)用到
-
渲染流水線中展辞,CPU 會(huì)負(fù)責(zé)哪些任務(wù)?
- 點(diǎn)擊事件的處理
- 顯示內(nèi)容的前置計(jì)算万牺,例如布局計(jì)算罗珍、圖片解碼等任務(wù)
-
離屏渲染為什么會(huì)有效率問題?
- 離屏渲染時(shí)由于 App 需要提前對部分內(nèi)容進(jìn)行額外的渲染并保存到 Offscreen Buffer脚粟,以及需要在必要時(shí)刻對 Offscreen Buffer 和 Framebuffer 進(jìn)行內(nèi)容切換覆旱,所以會(huì)需要更長的處理時(shí)間(實(shí)際上這兩步關(guān)于 buffer 的切換代價(jià)都非常大)
- 并且 Offscreen Buffer 本身就需要額外的空間,大量的離屏渲染可能早能內(nèi)存的過大壓力核无。與此同時(shí)扣唱,Offscreen Buffer 的總大小也有限,不能超過屏幕總像素的 2.5 倍团南。
- 可見離屏渲染的開銷非常大噪沙,一旦需要離屏渲染的內(nèi)容過多,很容易造成掉幀的問題吐根。所以大部分情況下正歼,我們都應(yīng)該盡量避免離屏渲染。
-
什么時(shí)候應(yīng)該使用離屏渲染拷橘?
- 一些特殊效果需要使用額外的 Offscreen Buffer 來保存渲染的中間狀態(tài)局义,所以不得不使用離屏渲染。
- 出于效率目的冗疮,可以將內(nèi)容提前渲染保存在 Offscreen Buffer 中萄唇,達(dá)到復(fù)用的目的。
-
shouldRasterize 光柵化是什么赌厅?
- 光柵化的目的就是強(qiáng)制開啟離屏渲染穷绵。
- 如果 layer 不能被復(fù)用,則沒有必要打開光柵化特愿;
- 如果 layer 不是靜態(tài)的仲墨,需要被頻繁修改勾缭,比如處于動(dòng)畫之中,那么開啟了離屏渲染反而會(huì)影響效率目养;
- 離屏渲染緩存內(nèi)容有時(shí)間限制俩由,緩存內(nèi)容 100ms 內(nèi)如果沒有被使用,那么它就會(huì)被丟棄調(diào)癌蚁,無法進(jìn)行復(fù)用幻梯;
- 離屏渲染緩存內(nèi)容有空間限制,超過 2.5 倍屏幕像素大小的話努释,也會(huì)失效碘梢,且無法進(jìn)行復(fù)用;
-
有哪些常見的觸發(fā)離屏渲染的情況伐蒂?
- 使用了 mask 的 layer(layer.mask)
- 需要進(jìn)行裁剪的 layer(layer.masksToBounds / view.clipsToBounds)
- 設(shè)置了組透明度為 YES煞躬,并且透明度不為1 的layer(layer.allosGroupOpacity / layer.opacity)
- 添加了投影的 layer(layer.shadow*)
- 繪制了文字的 layer(UILabel,CATextLayer逸邦,Core Text等)
-
cornerRadius 設(shè)置圓角會(huì)觸發(fā)離屏渲染嗎恩沛?
- 不會(huì)觸發(fā)。
- 設(shè)置了 maskToBounds(clipsToBounds)才有可能觸發(fā)
-
圓角觸發(fā)的離屏渲染有哪些解決方案缕减?
- 如果是圖片雷客,可以讓UI提供帶圓角的圖片
- 先將圖片圓角切好,然后使用切好圓角的圖片
- 如果是按鈕背景圖桥狡,可以直接設(shè)置imageview的圓角
- 繪制子layer搅裙,插入一個(gè)子layer作為顯示層(類比于按鈕上的imageview)
-
重寫 drawRect 方法會(huì)觸發(fā)離屏渲染嗎?
- 不會(huì)觸發(fā)離屏渲染
- drawRect 是在CPU中執(zhí)行的