UITableView相關
- 重用機制 - 復用池 - 復用池原理
//通過dequeueReusableCellWithIdentifier獲取cell就能得到UITableView的重用機制的支持
cell = [tableView dequeueReusableCellWithIdentifier:identifier];
-
多線程下修改或訪問數(shù)據(jù)源的同步解決方案
- 并發(fā)訪問,數(shù)據(jù)拷貝
主線程直接訪問數(shù)據(jù),并對數(shù)據(jù)操作進行記錄(如刪除);
異步線程獲取數(shù)據(jù)源的拷貝,進行數(shù)據(jù)操作,在返回主線程時,將主線程對操作的記錄(如刪除)對再進行一遍。
(記錄數(shù)據(jù)刪除;需要做數(shù)據(jù)源拷貝,對內(nèi)存會有一定開銷)
- 串行訪問(GCD 串行隊列)
將數(shù)據(jù)操作都添加到GCD的串行隊列中,由串行隊列保證數(shù)據(jù)的一致性
(由于主線程數(shù)據(jù)源操作也加入了串行隊列,如果之前有較耗時的操作時,可能主線程的數(shù)據(jù)操作(如刪除)會有一定的延遲)
UI圖像顯示原理
- UIView與CALayer的關系
UIView 的顯示部分是由
view.layer(CALayer)決定的,backgroundColor是對layer的同名屬性的封裝
UIView為其提供內(nèi)容,以及負責處理觸摸等事件,參與響應鏈
CALayer負責顯示內(nèi)容contents
在設計原則上,體現(xiàn)了單一職責原則 ,體現(xiàn)了UIView與CALayer在職責上的分工
UILabel繪制和渲染流程繪制和渲染的流程 (參考iOS繪制和渲染與iOS渲染)
- UI的布局(Layout)如:為視圖/圖層準備層級關系,以及設置圖層屬性(位置,背景色,邊框等等)的階段。(UI布局、文本計算);
- 顯示(Display)如:圖層的寄宿圖片被繪制的階段。繪制涉及到-drawRect:和-drawLayer:inContext:方法的調用;
- 一些其他的準備工作(Prepare)如: Image decoding, Image conversion(如果圖片類型不是GPU所支持的,需要對圖片進行轉換)。
- 提交GPU(Commit):Core Animation打包所有的圖層和動畫,然后通過IPC(進程內(nèi)通信)發(fā)送到渲染服務(render server,一個單獨管理動畫和圖層組合的一個系統(tǒng)進程)。這個步驟是遞歸的,所以如果layer tree如果比較復雜此步驟代價比較高
上面4個步驟發(fā)生在自己的應用程序內(nèi)部,動畫顯示到屏幕之前還有2個步驟的工作:
- 對所有圖層屬性計算中間值,設置OpenGL幾何形狀來執(zhí)行渲染。
- 在屏幕上渲染可見的三角形。
前5個階段都在軟件層面處理(通過CPU),只有最后一個階段被GPU執(zhí)行。6個階段中只有布局和顯示兩個階段是可以被我們控制的,Core Animation框架處理剩下的事務。
GPU(OpenGL)渲染管線()
- 定點著色、圖元裝配、光柵化、片段著色、片段處理
- 做完上訴操作后,將像素點提交到對應的幀緩存區(qū)當中(FrameBuffer)
- 最后由視頻控制器在VSync信號到來之前去幀緩沖區(qū)中提取要顯示的內(nèi)容
- 卡頓&掉幀
如果屏幕刷新率為60,那么將意味著需要在16.7ms(1000ms/60)內(nèi)需要將下一幀需要顯示的內(nèi)容準備好,由于垂直同步的機制,如果在一個 HSync(VSync) 時間內(nèi),CPU 或者 GPU 沒有完成內(nèi)容提交,則那一幀就會被丟棄,等待下一次機會再顯示,而這時顯示屏會保留之前的內(nèi)容不變。這就是界面卡頓的原因。
UI卡頓、掉幀原理UIView的繪制原理&異步繪制
UIView的繪制原理
UIView調用setNeedsDisplay方法其實是調用layer屬性的同名方法,這時layer并不會立刻調用display方法,而是要等到當前runloop即將結束的時候調用display,進入到繪制流程。在UIView中l(wèi)ayer.delegate就是UIView本身,UIView并沒有實現(xiàn)displayLayer:方法,所以進入系統(tǒng)的繪制流程,我們可以通過實現(xiàn) displayLayer: 方法來進行異步繪制。
系統(tǒng)繪制流程
系統(tǒng)繪制流程
CALayer會在內(nèi)部創(chuàng)建一個backing store(CGContextRef)();
判斷l(xiāng)ayer是否有代理;
如果有代理:調用delegete的drawLayer:inContext, 然后在合適的時機回調代理, 在[UIView drawRect]中做一些繪制工作;
如果沒有代理:調用layer的drawInContext方法;
最后無論是哪個分支最后都把 backing store 的 bitmap 位圖提交到 GPU,也就是將生成的 bitmap 位圖賦值給 layer.content 屬性。異步繪制流程
> 通過在自定義view的中實現(xiàn)CALayer的delegate方法 displayLayer: 來使用異步繪制,這時整個界面的顯示內(nèi)容都需要我們來繪制了(如UILabel中的文字也需要我們來繪制出來);
> 異步繪制過程中代理負責生成對應的位圖(bitmap);
> 將bitmap賦值給layer.content屬性;離屏渲染問題(iOS-離屏渲染詳解, iOS 關于離屏渲染的理解 以及解決方案)
GPU屏幕渲染有兩種方式:
(1)On-Screen Rendering (當前屏幕渲染) :指的是GPU的渲染操作是在當前用于顯示的屏幕緩沖區(qū)進行。
(2)Off-Screen Rendering (離屏渲染):指的是在GPU在當前屏幕緩沖區(qū)以外開辟一個緩沖區(qū)進行渲染操作。
相比于當前屏幕渲染,離屏渲染的代價是很高的,主要體現(xiàn)在兩個方面:
(1)創(chuàng)建新緩沖區(qū)(要想進行離屏渲染,首先要創(chuàng)建一個新的緩沖區(qū))。
(2)上下文切換何時會觸發(fā)
- 設置圓角layer.cornerRadius(并且maskToBounds=YES時)
- 為圖層設置遮罩(layer.mask)
- 為圖層設置陰影(layer.shadow *)
- 光柵化(shouldRasterize,將圖轉化為一個個柵格組成的圖象。 光柵化特點:每個元素對應幀緩沖區(qū)中的一像素)
為何要避免離屏渲染
- 離屏渲染需要創(chuàng)建新緩沖區(qū),并且整個過程需要多次切換上下文環(huán)境:先是從當前屏幕(On-Screen)切換到離屏(Off-Screen),等到離屏渲染結束以后,將離屏緩沖區(qū)的渲染結果顯示到屏幕上又需要將上下文環(huán)境從離屏切換到當前屏幕。而上下文環(huán)境的切換是要付出較大的消耗。
- 離屏渲染發(fā)生在GPU上,觸發(fā)了OpenGL上的多通道渲染管線,產(chǎn)生了額外開銷,加大了GPU的工作量,為避免CPU和GPU工作時間超過了一個刷新時(HSync、HSync),從而造成丟幀,所以應該盡量避免離屏渲染
UIScrollView、UITableView滑動優(yōu)化方案
減緩CPU的壓力 讓其有更多時間來處理用戶交互
- 對象的創(chuàng)建、調整、銷毀操作放到子線程去做(可以節(jié)省一部分CPU時間);
- 預拍版(UI的布局計算、文本計算放到子線程去做)
- 預渲染(文本等異步繪制、圖片編解碼等)
減緩GPU壓力
- 紋理渲染(離屏渲染如圓角、maskToBounds等會加大GPU的壓力,可以使用CPU的異步繪制機制來減少離屏渲染)
- 視圖混合(視圖層級復雜層層疊加,GPU需要做視圖混合,并且需要消耗大量的計算量來計算每一個像素值,可以通過減輕視圖層級復雜性或者通過CPU的異步繪制來讓位圖層級層級減少)
事件傳遞機制與視圖響應鏈
事件傳遞機制>
//最終那個視圖響應事件 - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event; //判斷點是否在當前視圖范圍 - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;hitTest: withEvent:方法內(nèi)部流程
hitTest: withEvent:方法內(nèi)部實現(xiàn)點擊屏幕的事件傳遞流程
事件傳遞流程
視圖事件響應
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event; - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event; - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;點擊在View C2上時,如果C2不響應事件,向上找父視圖B2,如果B2不響應,繼續(xù)向上找父視圖A,如果A不響應事件,將繼續(xù)沿著響應鏈傳遞如UIWindow,最后直到UIApplocation,如果最后仍然沒有視圖來處理事件,則這個事件就會被忽略
視圖事件響應



