iOS 界面渲染

嘗試和大家一起探討以下問題:

  1. view繪制渲染機制和runloop什么關(guān)系诺凡?
  2. 所謂的列表卡頓变屁,到底是什么原因引發(fā)的病瞳?
  3. 我們經(jīng)常在drawrect方法里繪制代碼揽咕,但該方法是誰調(diào)用的 何時調(diào)用的?
  4. drawrect方法內(nèi)為何第一行代碼往往要獲取圖形的上下文套菜?
  5. layer的代理必須是view嗎亲善,可以是vc嗎,為何CALayerDelegate 不能主動遵循逗柴?
  6. view繪制機制和CPU之間關(guān)系蛹头?
  7. view渲染機制和GPU之間關(guān)系?
  8. 所有的切圓角都很浪費性能嗎戏溺?
  9. 離屏渲染很nb嗎?
  10. 那些繪制API都是哪個類提供的 我如何系統(tǒng)的學(xué)習(xí)它掘而?
  11. 如何優(yōu)化CPU /GPU使用率?

view繪制渲染機制和runloop什么關(guān)系于购?

代碼示例

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    ZYYView *view = [[ZYYView alloc] init];
    view.backgroundColor = [UIColor whiteColor];
    view.bounds = CGRectMake(0, 0, 100, 100);
    view.center = CGPointMake(100, 100);
    [self.view addSubview:view];
}

@end
@implementation ZYYView

- (void)drawRect:(CGRect)rect {
    CGContextRef con = UIGraphicsGetCurrentContext();
    CGContextAddEllipseInRect(con, CGRectMake(0,0,100,200));
    CGContextSetRGBFillColor(con, 0, 0, 1, 1);
    CGContextFillPath(con);
}

@end

堆棧展示

這里寫圖片描述

底層原理

當(dāng)在操作 UI 時,比如改變了 Frame知染、更新了 UIView/CALayer 的層次時肋僧,或者手動調(diào)用了 UIView/CALayer 的 setNeedsLayout/setNeedsDisplay方法后,這個 UIView/CALayer 就被標(biāo)記為待處理控淡,并被提交到一個全局的容器去嫌吠。
蘋果注冊了一個 Observer 監(jiān)聽 BeforeWaiting(即將進入休眠) 和 Exit (即將退出Loop) 事件,回調(diào)去執(zhí)行一個很長的函數(shù):
_ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv()掺炭。這個函數(shù)里會遍歷所有待處理的 UIView/CAlayer 以執(zhí)行實際的繪制和調(diào)整辫诅,并更新 UI 界面。
這個函數(shù)內(nèi)部的調(diào)用棧大概是這樣的:

_ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv()
QuartzCore:CA::Transaction::observer_callback:
CA::Transaction::commit();
CA::Context::commit_transaction();
CA::Layer::layout_and_display_if_needed();
CA::Layer::layout_if_needed();
[CALayer layoutSublayers];
[UIView layoutSubviews];
CA::Layer::display_if_needed();
[CALayer display];
[UIView drawRect];

我們上圖的堆棧信息 截圖 涧狮,看到巴拉巴拉一大堆調(diào)用堆棧信息炕矮,其實這就是個函數(shù)做的孽 么夫。如何不能理解,那直接看下面的流程圖吧肤视。

流程圖

<svg height="1359.75" version="1.1" width="1014.421875" xmlns="http://www.w3.org/2000/svg" style="overflow: hidden; position: relative; top: -0.671875px;"><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="stt" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,305.0547,18.3125)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">程序啟動</tspan></text></svg> <text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op1t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,268.5547,125.5)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">UIApplicationMain()</tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op2t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,162.6172,232.6875)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">主線程:我是UI線程不能停档痪,Runloop來和我一起吧。</tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op3t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,241.0938,339.875)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">MainRunloop create and run </tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op4t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,154.3359,447.0625)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">MainRunloop:我想睡覺了邢滑,observer腐螟,你那邊有事嗎?</tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op5t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,4,554.25)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">observer:我去檢查一下_ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv()</tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op6t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,205.9922,661.4375)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">我去看看 圖層樹中有沒有待處理的對象</tspan></text><path fill="#ffffff" stroke="#000000" d="M33.75,16.875L0,33.75L67.5,67.5L135,33.75L67.5,0L0,33.75" stroke-width="2" font-family="sans-serif" font-weight="normal" id="cond" class="flowchart" transform="matrix(1,0,0,1,277.5547,754.3125)" style="font-family: sans-serif; font-weight: normal;"></path><text x="38.75" y="33.75" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="condt" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,277.5547,754.3125)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">有沒有困后?</tspan><tspan dy="18" x="38.75"></tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op7t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,162.4609,890.125)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">CPU:我在更新圖層樹乐纸,一會交給Core Animation運走</tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op8t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,118.6328,997.3125)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">Core Animation:把待處理的圖層對象 通過IPC發(fā)送到渲染服務(wù)進程</tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op9t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,228.4844,1104.5)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">GPU:渲染服務(wù)進程開始渲染工作</tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op10t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,205.8906,1211.6875)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">GPU:Compositing\Offscreen Rendering </tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op11t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,297.5547,1318.875)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">展示到屏幕 </tspan></text><text x="20" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="sub1t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,602.7969,768.625)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">告訴runloop 讓它睡會吧。有東西,我在叫你observer宇整。</tspan></text><path fill="none" stroke="#000000" d="M345.0546875,57.1875C345.0546875,57.1875,345.0546875,109.31428690254688,345.0546875,122.49869882418716" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><path fill="none" stroke="#000000" d="M345.0546875,164.375C345.0546875,164.375,345.0546875,216.50178690254688,345.0546875,229.68619882418716" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><path fill="none" stroke="#000000" d="M345.0546875,271.5625C345.0546875,271.5625,345.0546875,323.6892869025469,345.0546875,336.87369882418716" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><path fill="none" stroke="#000000" d="M345.0546875,378.75C345.0546875,378.75,345.0546875,430.8767869025469,345.0546875,444.06119882418716" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><path fill="none" stroke="#000000" d="M345.0546875,485.9375C345.0546875,485.9375,345.0546875,538.0642869025469,345.0546875,551.2486988241872" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><path fill="none" stroke="#000000" d="M345.0546875,593.125C345.0546875,593.125,345.0546875,645.2517869025469,345.0546875,658.4361988241872" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><path fill="none" stroke="#000000" d="M345.0546875,700.3125C345.0546875,700.3125,345.0546875,739.9665999412537,345.0546875,751.3129390846007" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><path fill="none" stroke="#000000" d="M345.0546875,821.8125C345.0546875,821.8125,345.0546875,873.9392869025469,345.0546875,887.1236988241872" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><text x="350.0546875" y="831.8125" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" font-size="14px" font-family="sans-serif" font-weight="normal" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">yes</tspan></text><path fill="none" stroke="#000000" d="M412.5546875,788.0625C412.5546875,788.0625,575.5197486574762,788.0625,599.7894477068073,788.0625" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><text x="417.5546875" y="778.0625" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" font-size="14px" font-family="sans-serif" font-weight="normal" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">no</tspan></text><path fill="none" stroke="#000000" d="M345.0546875,929C345.0546875,929,345.0546875,981.1267869025469,345.0546875,994.3111988241872" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><path fill="none" stroke="#000000" d="M345.0546875,1036.1875C345.0546875,1036.1875,345.0546875,1088.3142869025469,345.0546875,1101.4986988241872" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><path fill="none" stroke="#000000" d="M345.0546875,1143.375C345.0546875,1143.375,345.0546875,1195.5017869025469,345.0546875,1208.6861988241872" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><path fill="none" stroke="#000000" d="M345.0546875,1250.5625C345.0546875,1250.5625,345.0546875,1302.6892869025469,345.0546875,1315.8736988241872" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path>

所謂的列表卡頓于毙,到底是什么原因引發(fā)的?

iOS的mainRunloop是一個60fps的回調(diào)庶喜,也就是說每16.7ms會繪制一次屏幕,這個時間段內(nèi)要完成view的緩沖區(qū)創(chuàng)建救鲤,view內(nèi)容的繪制(如果重寫了drawRect)久窟,這些CPU的工作。然后將這個緩沖區(qū)交給GPU渲染本缠,這個過程又包括多個view的拼接(compositing)斥扛,紋理的渲染(Texture)等,最終顯示在屏幕上丹锹。整個過程就是我們上面畫的流程圖稀颁。 因此,如果在16.7ms內(nèi)完不成這些操作楣黍,比如匾灶,CPU做了太多的工作,或者view層次過于多租漂,圖片過于大阶女,導(dǎo)致GPU壓力太大,就會導(dǎo)致“卡”的現(xiàn)象哩治,也就是丟幀

我們經(jīng)常在drawrect方法里繪制代碼秃踩,但該方法是誰調(diào)用的 何時調(diào)用的?

產(chǎn)品繪圖需求

首先我們假設(shè)有這樣一個需求:實現(xiàn)下面的橢圓效果:


這里寫圖片描述

代碼示例

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    ZYYView *view = [[ZYYView alloc] init];
    view.backgroundColor = [UIColor whiteColor];
    view.bounds = CGRectMake(0, 0, 100, 100);
    view.center = CGPointMake(100, 100);
    [self.view addSubview:view];
}

@end
@implementation ZYYView

- (void)drawRect:(CGRect)rect {
    CGContextRef con = UIGraphicsGetCurrentContext();
    CGContextAddEllipseInRect(con, CGRectMake(0,0,100,200));
    CGContextSetRGBFillColor(con, 0, 0, 1, 1);
    CGContextFillPath(con);
}

@end

堆棧展示

這里寫圖片描述

底層原理

1业筏、 在[ZYYView drawRect:] 方法之前憔杨,先調(diào)用了 [UIView(CALayerDelegate) drawLayer:inContext:] 和 [CALayer drawInContext:]
2、如果 [self.view addSubview:view]; 被注銷掉 則 drawRect 不執(zhí)行蒜胖∠穑可以肯定 drawRect
方法是由 addSubview 函數(shù)觸發(fā)的抛蚤。

流程圖

<svg height="323.5" version="1.1" width="355.578125" xmlns="http://www.w3.org/2000/svg" style="overflow: hidden; position: relative; top: -0.4375px;"><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="stt" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,73.2344,4)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">[self.view addSubview:view]</tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op1t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,82.5313,96.875)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">[CALayer drawInContext:]</tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op2t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,4,189.75)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">[UIView(CALayerDelegate) drawLayer:inContext:]</tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="et" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,99.9531,282.625)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">[ZYYView drawRect:]</tspan></text></svg>

drawrect方法內(nèi)為何第一行代碼總要獲取圖形的上下文

代碼示例

CGContextRef con = UIGraphicsGetCurrentContext();

堆棧展示

這里寫圖片描述

底層原理
每一個UIView都有一個layer,每一個layer都有個content妖啥,這個content指向的是一塊緩存霉颠,叫做backing store
當(dāng)UIView被繪制時(從 CA::Transaction::commit:以后),CPU執(zhí)行drawRect荆虱,通過context將數(shù)據(jù)寫入backing store
當(dāng)backing store寫完后蒿偎,通過render server交給GPU去渲染,將backing store中的bitmap數(shù)據(jù)顯示在屏幕上
所以在 drawRect 方法中 要首先獲取 context

layer的代理必須是view嗎怀读,可以是vc嗎诉位?為何CALayerDelegate 不能主動遵循?

代碼示例

代碼1

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    ZYYLayer *layer = [ZYYLayer layer];
    layer.bounds    = CGRectMake(0, 0, 100, 100);
    layer.position  = CGPointMake(100, 100);
    [layer setNeedsDisplay];
    [self.view.layer addSublayer:layer];
}

@end
@implementation ZYYLayer

- (void)drawInContext:(CGContextRef)ctx {
    CGContextAddEllipseInRect(ctx, CGRectMake(0,0,100,200));
    CGContextSetRGBFillColor(ctx, 0, 0, 1, 1);
    CGContextFillPath(ctx);
}

@end

代碼二

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController

@end
#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    CALayer *layer  = [CALayer layer];
    layer.bounds    = CGRectMake(0, 0, 100, 100);
    layer.position  = CGPointMake(100, 100);
    layer.delegate  = self;
    [layer setNeedsDisplay];
    [self.view.layer addSublayer:layer];
}

- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {
    CGContextAddEllipseInRect(ctx, CGRectMake(0,0,100,200));
    CGContextSetRGBFillColor(ctx, 0, 0, 1, 1);
    CGContextFillPath(ctx);
}

@end

圖標(biāo)展示

綜合以上2種不同的繪制函數(shù)加上uiview下的drawrect方法 一起區(qū)別 :

編號 所在的類或類別 方法 出現(xiàn)范圍 可以使用的API viewDidLoad 優(yōu)先級
1 UIView(UIViewRendering) drawRect 自定義view類 UIkit 菜枷、CoreGraphics 3
2 CALayer drawInContext 自定義layer類 CoreGraphics [layer setNeedsDisplay] 1
3 NSObject (CALayerDelegate) drawLayer:inContext vc類苍糠、自定義layer、view類 UIkit啤誊、CoreGraphics [layer setNeedsDisplay] layer.delegate = self 2

底層原理

這里寫圖片描述

不能再將某個UIView設(shè)置為CALayer的delegate岳瞭,因為UIView對象已經(jīng)是它內(nèi)部根層的delegate,再次設(shè)置為其他層的delegate就會出問題蚊锹。

這里寫圖片描述

在設(shè)置代理的時候瞳筏,它并不要求我們遵守協(xié)議,說明這個方法為非正式協(xié)議牡昆,就不需要再額外的顯示遵守協(xié)議了

view繪制機制和CPU之間關(guān)系

創(chuàng)建對象

性能瓶頸:

創(chuàng)建對象會分配內(nèi)存姚炕,對象過多,比較消耗 CPU 資源 丢烘。

優(yōu)化方案:

1柱宦、盡量用輕量的對象代替重量的對象,可以對性能有所優(yōu)化播瞳。比如 CALayer 比 UIView 要輕量掸刊,如果不需要響應(yīng)觸摸事件,用 CALayer 顯示會更加合適赢乓。如果對象不涉及 UI 操作忧侧,則盡量放到后臺線程去創(chuàng)建,但如果是包含了 CALayer 的控件骏全,都只能在主線程創(chuàng)建和操作。
2尼斧、通過 Storyboard 創(chuàng)建視圖對象時姜贡,其資源消耗會比直接通過代碼創(chuàng)建對象要大非常多。
3棺棵、使用懶加載楼咳,盡量推遲對象創(chuàng)建的時間熄捍,并把對象的創(chuàng)建分散到多個任務(wù)中去。

調(diào)整對象

調(diào)整對象視圖層級

性能瓶頸:

對象的調(diào)整也經(jīng)常是消耗 CPU 資源的地方母怜。視圖層次調(diào)整時余耽,UIView、CALayer 之間會出現(xiàn)很多方法調(diào)用與通知苹熏。

優(yōu)化方案:

盡量的避免或者減少調(diào)整視圖層次碟贾、添加和移除視圖。

調(diào)整對象布局計算

性能瓶頸:視圖布局的計算是 App 中最為常見的消耗 CPU 資源的地方

優(yōu)化方案:不論通過何種技術(shù)對視圖進行布局轨域,其最終都會落到對 UIView.frame/bounds/center 等屬性的調(diào)整上袱耽。對這些屬性的調(diào)整非常消耗資源,所以盡量提前計算好布局干发,如果一次性可以調(diào)整好對應(yīng)屬性朱巨,就不要多次、頻繁的計算和調(diào)整這些屬性枉长。

調(diào)整對象文本計算

性能瓶頸:如果一個界面中包含大量文本(比如微博微信朋友圈等)冀续,文本的寬高計算會占用很大一部分資源。

優(yōu)化方案:用 [NSAttributedString boundingRectWithSize:options:context:] 來計算文本寬高必峰,用 -[NSAttributedString drawWithRect:options:context:] 來繪制文本洪唐,記住放到后臺線程進行以避免阻塞主線程。

圖像的繪制

流程圖

<svg height="4560.33984375" version="1.1" width="1263.32421875" xmlns="http://www.w3.org/2000/svg" style="overflow: hidden; position: relative; top: -0.078125px;"><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="stt" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,325.5547,175.5313)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">自定義ZYYView</tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op1t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,330.9375,439.9375)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">初始化自点、坐標(biāo)</tspan></text><text x="70.923828125" y="65.923828125" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="c1t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,254.0898,657.8574)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.330078125">確認(rèn) alloc setFrame桐罕?</tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op2t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,278.1328,1015.2363)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">[self.view addSubview:view];</tspan></text></svg> <path fill="#ffffff" stroke="#000000" d="M60.603515625,30.3017578125L0,60.603515625L121.20703125,121.20703125L242.4140625,60.603515625L121.20703125,0L0,60.603515625" stroke-width="2" font-family="sans-serif" font-weight="normal" id="c2" class="flowchart" transform="matrix(1,0,0,1,264.7305,1238.4766)" style="font-family: sans-serif; font-weight: normal;"></path><text x="65.603515625" y="60.603515625" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="c2t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,264.7305,1238.4766)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.337890625">確認(rèn) addsubview ?</tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op3t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,196.6172,1585.2148)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">(隱式) 此view的layer的CALayerDelegate設(shè)置成此view</tspan></text><text x="20" y="28.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="sub3t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,127.1094,1840.6211)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="-3.671875">1桂敛、首先CPU會為layer分配一塊內(nèi)存用來繪制bitmap功炮,叫做backing store</tspan><tspan dy="18" x="20">2、layer創(chuàng)建指向這塊bitmap緩沖區(qū)的指針术唬,叫做CGContextRef</tspan><tspan dy="18" x="20"></tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op4t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,224.4375,2123.0273)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">調(diào)用此view的self.layer的drawInContext:方法</tspan><tspan dy="18" x="10"></tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op5t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,221.6016,2387.4336)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">執(zhí)行 - (void)drawInContext:(CGContextRef)ctx</tspan></text><path fill="#ffffff" stroke="#000000" d="M190.96875,95.484375L0,190.96875L381.9375,381.9375L763.875,190.96875L381.9375,0L0,190.96875" stroke-width="2" font-family="sans-serif" font-weight="normal" id="c3" class="flowchart" transform="matrix(1,0,0,1,4,2480.3086)" style="font-family: sans-serif; font-weight: normal;"></path><text x="195.96875" y="190.96875" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="c3t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,4,2480.3086)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">if([self.delegate responseToSelector:@selector(drawLayer:inContext:)])</tspan><tspan dy="18" x="195.96875"></tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op6t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,150.5156,3087.7773)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">執(zhí)行(void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx</tspan></text><path fill="#ffffff" stroke="#000000" d="M129.09375,64.546875L0,129.09375L258.1875,258.1875L516.375,129.09375L258.1875,0L0,129.09375" stroke-width="2" font-family="sans-serif" font-weight="normal" id="c4" class="flowchart" transform="matrix(1,0,0,1,127.75,3242.5273)" style="font-family: sans-serif; font-weight: normal;"></path><text x="134.09375" y="129.09375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="c4t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,127.75,3242.5273)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">是否調(diào)用[super drawLayer:layer inContext:ctx] </tspan><tspan dy="18" x="134.09375"></tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op7t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,259.0391,3726.2461)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">執(zhí)行 - (void)drawRect:(CGRect)rect</tspan><tspan dy="18" x="10"></tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op8t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,181.6406,3990.6523)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">使用 UIkit繪制API 或者 CoreGraphics繪制API薪伏,繪制bitmap</tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="op9t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,256.6641,4255.0586)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">將layer的content指向生成的bitmap</tspan></text><text x="10" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="et" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,295.6328,4519.4648)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">交付layer的content屬性</tspan><tspan dy="18" x="10"></tspan></text><text x="20" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="sub2t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,853.668,1279.6426)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">drawrect 方法不調(diào)用</tspan><tspan dy="18" x="20"></tspan></text><text x="20" y="19.4375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" id="sub1t" class="flowchartt" font-size="14px" font-family="sans-serif" font-weight="normal" transform="matrix(1,0,0,1,1082.4961,704.3438)" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">drawrect 方法不調(diào)用</tspan><tspan dy="18" x="20"></tspan></text><path fill="none" stroke="#000000" d="M385.9375,214.40625C385.9375,214.40625,385.9375,410.25966608710587,385.9375,436.9408687669411" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><path fill="none" stroke="#000000" d="M385.9375,478.8125C385.9375,478.8125,385.9375,631.4584060381167,385.9375,654.8621240537263" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><path fill="none" stroke="#000000" d="M385.9375,789.705078125C385.9375,789.705078125,385.9375,985.5584942121059,385.9375,1012.2396968919411" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><text x="390.9375" y="799.705078125" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" font-size="14px" font-family="sans-serif" font-weight="normal" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.330078125">yes</tspan></text><path fill="none" stroke="#000000" d="M517.78515625,723.78125C517.78515625,723.78125,1035.272804287728,723.78125,1079.4917195253267,723.78125" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><text x="522.78515625" y="713.78125" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" font-size="14px" font-family="sans-serif" font-weight="normal" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.328125">no</tspan></text><path fill="none" stroke="#000000" d="M385.9375,1054.111328125C385.9375,1054.111328125,385.9375,1211.6257607354783,385.9375,1235.4682773903846" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><path fill="none" stroke="#000000" d="M385.9375,1359.68359375C385.9375,1359.68359375,385.9375,1555.5370098371059,385.9375,1582.218212516941" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><text x="390.9375" y="1369.68359375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" font-size="14px" font-family="sans-serif" font-weight="normal" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.33203125">yes</tspan></text><path fill="none" stroke="#000000" d="M507.14453125,1299.080078125C507.14453125,1299.080078125,816.8033232688904,1299.080078125,850.6722536459565,1299.080078125" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><text x="512.14453125" y="1289.080078125" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" font-size="14px" font-family="sans-serif" font-weight="normal" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.330078125">no</tspan></text><path fill="none" stroke="#000000" d="M385.9375,1624.08984375C385.9375,1624.08984375,385.9375,1811.5368828047067,385.9375,1837.6220420481595" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><path fill="none" stroke="#000000" d="M385.9375,1897.49609375C385.9375,1897.49609375,385.9375,2093.349509837106,385.9375,2120.030712516941" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><path fill="none" stroke="#000000" d="M385.9375,2161.90234375C385.9375,2161.90234375,385.9375,2357.755759837106,385.9375,2384.436962516941" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><path fill="none" stroke="#000000" d="M385.9375,2426.30859375C385.9375,2426.30859375,385.9375,2465.9626936912537,385.9375,2477.3090328346007" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><path fill="none" stroke="#000000" d="M385.9375,2862.24609375C385.9375,2862.24609375,385.9375,3058.099509837106,385.9375,3084.780712516941" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><text x="390.9375" y="2872.24609375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" font-size="14px" font-family="sans-serif" font-weight="normal" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.33203125">yes</tspan></text><path fill="none" stroke="#000000" d="M767.875,2671.27734375C767.875,2671.27734375,792.875,2671.27734375,792.875,2671.27734375C792.875,2671.27734375,792.875,4494.46484375,792.875,4494.46484375C792.875,4494.46484375,385.9375,4494.46484375,385.9375,4494.46484375C385.9375,4494.46484375,385.9375,4509.83828830719,385.9375,4516.474091524258" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><text x="772.875" y="2661.27734375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" font-size="14px" font-family="sans-serif" font-weight="normal" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.33203125">no</tspan></text><path fill="none" stroke="#000000" d="M385.9375,3126.65234375C385.9375,3126.65234375,385.9375,3221.3582960292697,385.9375,3239.531850348485" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><path fill="none" stroke="#000000" d="M385.9375,3500.71484375C385.9375,3500.71484375,385.9375,3696.568259837106,385.9375,3723.249462516941" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><text x="390.9375" y="3510.71484375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" font-size="14px" font-family="sans-serif" font-weight="normal" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.33203125">yes</tspan></text><path fill="none" stroke="#000000" d="M644.125,3371.62109375C644.125,3371.62109375,669.125,3371.62109375,669.125,3371.62109375C669.125,3371.62109375,669.125,4494.46484375,669.125,4494.46484375C669.125,4494.46484375,385.9375,4494.46484375,385.9375,4494.46484375C385.9375,4494.46484375,385.9375,4509.83828830719,385.9375,4516.474091524258" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><text x="649.125" y="3361.62109375" text-anchor="start" font="10px "Arial"" stroke="none" fill="#000000" font-size="14px" font-family="sans-serif" font-weight="normal" style="text-anchor: start; font-style: normal; font-variant-caps: normal; font-weight: normal; font-size: 14px; line-height: normal; font-family: sans-serif;"><tspan dy="5.33203125">no</tspan></text><path fill="none" stroke="#000000" d="M385.9375,3765.12109375C385.9375,3765.12109375,385.9375,3960.974509837106,385.9375,3987.655712516941" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><path fill="none" stroke="#000000" d="M385.9375,4029.52734375C385.9375,4029.52734375,385.9375,4225.380759837106,385.9375,4252.061962516941" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path><path fill="none" stroke="#000000" d="M385.9375,4293.93359375C385.9375,4293.93359375,385.9375,4489.787009837106,385.9375,4516.468212516941" stroke-width="2" marker-end="url(#raphael-marker-endblock33)" font-family="sans-serif" font-weight="normal" style="font-family: sans-serif; font-weight: normal;"></path>

底層原理

我們回過頭思考 圖形的上下文 CGContextRef的創(chuàng)建歷程。

? addsubview 的時候 觸發(fā)的
? CPU會為layer分配一塊內(nèi)存用來繪制bitmap粗仓,叫做backing store
? layer創(chuàng)建指向這塊bitmap緩沖區(qū)的指針嫁怀,叫做CGContextRef
? 通過CoreGraphic的api,也叫Quartz2D借浊,繪制bitmap
? 將layer的content指向生成的bitmap

其實 CGContextRef 的創(chuàng)建過程 就是CPU的工作過程
CPU 將view變成了bitmap 完成自己工作塘淑,剩下就是GPU的工作了。

view渲染機制和GPU之間關(guān)系

GPU功能

GPU處理的單位是Texture
基本上我們控制GPU都是通過OpenGL來完成的蚂斤,但是從bitmap到Texture之間需要一座橋梁存捺,Core Animation正好充當(dāng)了這個角色:
Core Animation對OpenGL的api有一層封裝,當(dāng)我們的要渲染的layer已經(jīng)有了bitmap content的時候,這個content一般來說是一個CGImageRef捌治,CoreAnimation會創(chuàng)建一個OpenGL的Texture并將CGImageRef(bitmap)和這個Texture綁定岗钩,通過TextureID來標(biāo)識。
這個對應(yīng)關(guān)系建立起來之后肖油,剩下的任務(wù)就是GPU如何將Texture渲染到屏幕上了兼吓。

GPU工作模式:

整個過程也就是一件事:CPU將準(zhǔn)備好的bitmap放到RAM里,GPU去搬這快內(nèi)存到VRAM中處理森枪。
而這個過程GPU所能承受的極限大概在16.7ms完成一幀的處理视搏,所以最開始提到的60fps其實就是GPU能處理的最高頻率。

GPU性能瓶頸

因此疲恢,GPU的挑戰(zhàn)有兩個:
? 將數(shù)據(jù)從RAM搬到VRAM中
? 將Texture渲染到屏幕上
這兩個中瓶頸基本在第二點上凶朗。渲染Texture基本要處理這么幾個問題:

Compositing:

Compositing是指將多個紋理拼到一起的過程,對應(yīng)UIKit显拳,是指處理多個view合到一起的情況棚愤,如

[self.view addsubview : subview]

如果view之間沒有疊加,那么GPU只需要做普通渲染即可杂数。 如果多個view之間有疊加部分宛畦,GPU需要做blending。
加入兩個view大小相同揍移,一個疊加在另一個上面次和,那么計算公式如下:

R = S+D*(1-Sa)

R: 為最終的像素值
S: 代表 上面的Texture(Top Texture)
D: 代表下面的Texture(lower Texture)
Sa代表Texture的alpha值。
其中S,D都已經(jīng)pre-multiplied各自的alpha值那伐。
假如Top Texture(上層view)的alpha值為1踏施,即不透明。那么它會遮住下層的Texture罕邀。即,R = S畅形。是合理的。 假如Top Texture(上層view)的alpha值為0.5诉探,S 為 (1,0,0)日熬,乘以alpha后為(0.5,0,0)。D為(0肾胯,0竖席,1)。 得到的R為(0.5敬肚,0毕荐,0.5)。
基本上每個像素點都需要這么計算一次艳馒。
因此憎亚,view的層級很復(fù)雜,或者view都是半透明的(alpha值不為1)都會帶來GPU額外的計算工作。
應(yīng)用應(yīng)當(dāng)盡量減少視圖數(shù)量和層次虽填,并在不透明的視圖里標(biāo)明 opaque 屬性以避免無用的 Alpha 通道合成。

Size

這個問題曹动,主要是處理image帶來的斋日,假如內(nèi)存里有一張400x400的圖片,要放到100x100的imageview里墓陈,如果不做任何處理恶守,直接丟進去,問題就大了贡必,這意味著兔港,GPU需要對大圖進行縮放到小的區(qū)域顯示,需要做像素點的sampling仔拟,這種smapling的代價很高衫樊,又需要兼顧pixel alignment。計算量會飆升利花。

shouldRasterize

其中shouldRasterize(光柵化)是比較特別的一種:
光柵化概念:將圖轉(zhuǎn)化為一個個柵格組成的圖象科侈。
光柵化特點:每個元素對應(yīng)幀緩沖區(qū)中的一像素。

shouldRasterize = YES在其他屬性觸發(fā)離屏渲染的同時炒事,會將光柵化后的內(nèi)容緩存起來臀栈,如果對應(yīng)的layer及其sublayers沒有發(fā)生改變,在下一幀的時候可以直接復(fù)用挠乳。shouldRasterize = YES权薯,這將隱式的創(chuàng)建一個位圖,各種陰影遮罩等效果也會保存到位圖中并緩存起來睡扬,從而減少渲染的頻度(不是矢量圖)盟蚣。

相當(dāng)于光柵化是把GPU的操作轉(zhuǎn)到CPU上了,生成位圖緩存威蕉,直接讀取復(fù)用刁俭。

當(dāng)你使用光柵化時,你可以開啟“Color Hits Green and Misses Red”來檢查該場景下光柵化操作是否是一個好的選擇韧涨。綠色表示緩存被復(fù)用牍戚,紅色表示緩存在被重復(fù)創(chuàng)建。

如果光柵化的層變紅得太頻繁那么光柵化對優(yōu)化可能沒有多少用處虑粥。位圖緩存從內(nèi)存中刪除又重新創(chuàng)建得太過頻繁如孝,紅色表明緩存重建得太遲∶浯可以針對性的選擇某個較小而較深的層結(jié)構(gòu)進行光柵化第晰,來嘗試減少渲染時間。

注意:
對于經(jīng)常變動的內(nèi)容,這個時候不要開啟,否則會造成性能的浪費

例如我們?nèi)粘探?jīng)常打交道的TableViewCell,因為TableViewCell的重繪是很頻繁的(因為Cell的復(fù)用),如果Cell的內(nèi)容不斷變化,則Cell需要不斷重繪,如果此時設(shè)置了cell.layer可光柵化。則會造成大量的離屏渲染,降低圖形性能茁瘦。

Offscreen Rendering And Mask(離屏渲染)

GPU屏幕渲染有以下兩種方式:

On-Screen Rendering
意為當(dāng)前屏幕渲染品抽,指的是GPU的渲染操作是在當(dāng)前用于顯示的屏幕緩沖區(qū)中進行。

Off-Screen Rendering
意為離屏渲染甜熔,指的是GPU在當(dāng)前屏幕緩沖區(qū)以外新開辟一個緩沖區(qū)進行渲染操作圆恤。

設(shè)置了以下屬性時,都會觸發(fā)離屏繪制:

shouldRasterize(光柵化)
masks(遮罩)
shadows(陰影)
edge antialiasing(抗鋸齒)
group opacity(不透明)
復(fù)雜形狀設(shè)置圓角等
漸變

為什么會使用離屏渲染

當(dāng)使用圓角腔稀,陰影盆昙,遮罩的時候,圖層屬性的混合體被指定為在未預(yù)合成之前不能直接在屏幕中繪制焊虏,所以就需要屏幕外渲染被喚起淡喜。

屏幕外渲染并不意味著軟件繪制,但是它意味著圖層必須在被顯示之前在一個屏幕外上下文中被渲染(不論CPU還是GPU)诵闭。

性能瓶頸:

如果我們對layer做這樣的操作:

label.layer.cornerRadius  = 5.0f;
label.layer.masksToBounds = YES;

會產(chǎn)生offscreen rendering,它帶來的最大的問題是炼团,當(dāng)渲染這樣的layer的時候,需要額外開辟內(nèi)存疏尿,繪制好radius们镜,mask,然后再將繪制好的bitmap重新賦值給layer润歉。所以當(dāng)使用離屏渲染的時候會很容易造成性能消耗模狭,屏幕外緩沖區(qū)跟當(dāng)前屏幕緩沖區(qū)上下文切換是很耗性能的。

優(yōu)化方案:

1踩衩、因此繼續(xù)性能的考慮嚼鹉,Quartz提供了優(yōu)化的api:

  label.layer.cornerRadius       = 5.0f;
  label.layer.masksToBounds      = YES;
  label.layer.shouldRasterize    = YES;
  label.layer.rasterizationScale = label.layer.contentsScale;

簡單的說,這是一種cache機制驱富。
2锚赤、只需要圓角的某些場合,也可以用一張已經(jīng)繪制好的圓角圖片覆蓋到原本視圖上面來模擬相同的視覺效果褐鸥。
3线脚、最徹底的解決辦法,就是把需要顯示的圖形在后臺線程繪制為圖片叫榕,避免使用圓角浑侥、陰影、遮罩等屬性.

同樣GPU的性能也可以通過instrument去衡量:

這里寫圖片描述

紅色代表GPU需要做額外的工作來渲染View晰绎,綠色代表GPU無需做額外的工作來處理bitmap寓落。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市荞下,隨后出現(xiàn)的幾起案子伶选,更是在濱河造成了極大的恐慌史飞,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件仰税,死亡現(xiàn)場離奇詭異构资,居然都是意外死亡,警方通過查閱死者的電腦和手機陨簇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門蚯窥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人塞帐,你說我怎么就攤上這事∥∩常” “怎么了葵姥?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長句携。 經(jīng)常有香客問我榔幸,道長,這世上最難降的妖魔是什么矮嫉? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任削咆,我火速辦了婚禮,結(jié)果婚禮上蠢笋,老公的妹妹穿的比我還像新娘拨齐。我一直安慰自己,他們只是感情好昨寞,可當(dāng)我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布瞻惋。 她就那樣靜靜地躺著,像睡著了一般援岩。 火紅的嫁衣襯著肌膚如雪歼狼。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天享怀,我揣著相機與錄音羽峰,去河邊找鬼。 笑死添瓷,一個胖子當(dāng)著我的面吹牛梅屉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播鳞贷,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼履植,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了悄晃?” 一聲冷哼從身側(cè)響起玫霎,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤凿滤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后庶近,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體翁脆,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年鼻种,在試婚紗的時候發(fā)現(xiàn)自己被綠了反番。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡叉钥,死狀恐怖罢缸,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情投队,我是刑警寧澤枫疆,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站敷鸦,受9級特大地震影響息楔,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜扒披,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一值依、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧碟案,春花似錦愿险、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至熔任,卻和暖如春褒链,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背疑苔。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工甫匹, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人惦费。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓兵迅,卻偏偏與公主長得像,于是被迫代替她去往敵國和親薪贫。 傳聞我的和親對象是個殘疾皇子恍箭,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,033評論 2 355

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