iOS-Core-Animation-Advanced-Techniques(十五:圖層性能)

本文轉(zhuǎn)載自:http://www.cocoachina.com/ios/20150106/10840.html? 為了防止cocochina以后刪除該文章畦幢,故轉(zhuǎn)載至此;

圖層性能

要更快性能缆蝉,也要做對正確的事情宇葱。 ——Stephen R. Covey

在第14章『圖像IO』討論如何高效地載入和顯示圖像瘦真,通過視圖來避免可能引起動畫幀率下降的性能問題。在最后一章黍瞧,我們將著重圖層樹本身诸尽,以發(fā)掘最好的性能。

隱式繪制

寄宿圖可以通過Core Graphics直接繪制印颤,也可以直接載入一個圖片文件并賦值給contents屬性您机,或事先繪制一個屏幕之外的CGContext上下文。在之前的兩章中我們討論了這些場景下的優(yōu)化膀哲。但是除了常見的顯式創(chuàng)建寄宿圖往产,你也可以通過以下三種方式創(chuàng)建隱式的:1,使用特性的圖層屬性某宪。2仿村,特定的視圖。3兴喂,特定的圖層子類蔼囊。

了解這個情況為什么發(fā)生何時發(fā)生是很重要的,它能夠讓你避免引入不必要的軟件繪制行為衣迷。

文本

CATextLayer和UILabel都是直接將文本繪制在圖層的寄宿圖中畏鼓。事實上這兩種方式用了完全不同的渲染方式:在iOS 6及之前,UILabel用WebKit的HTML渲染引擎來繪制文本壶谒,而CATextLayer用的是Core Text.后者渲染更迅速云矫,所以在所有需要繪制大量文本的情形下都優(yōu)先使用它吧。但是這兩種方法都用了軟件的方式繪制汗菜,因此他們實際上要比硬件加速合成方式要慢让禀。

不論如何,盡可能地避免改變那些包含文本的視圖的frame陨界,因為這樣做的話文本就需要重繪巡揍。例如,如果你想在圖層的角落里顯示一段靜態(tài)的文本菌瘪,但是這個圖層經(jīng)常改動腮敌,你就應該把文本放在一個子圖層中。

光柵化

在第四章『視覺效果』中我們提到了CALayer的shouldRasterize屬性俏扩,它可以解決重疊透明圖層的混合失靈問題糜工。同樣在第12章『速度的曲調(diào)』中,它也是作為繪制復雜圖層樹結(jié)構(gòu)的優(yōu)化方法录淡。

啟用shouldRasterize屬性會將圖層繪制到一個屏幕之外的圖像捌木。然后這個圖像將會被緩存起來并繪制到實際圖層的contents和子圖層。如果有很多的子圖層或者有復雜的效果應用赁咙,這樣做就會比重繪所有事務的所有幀劃得來得多钮莲。但是光柵化原始圖像需要時間,而且還會消耗額外的內(nèi)存彼水。

當我們使用得當時崔拥,光柵化可以提供很大的性能優(yōu)勢(如你在第12章所見),但是一定要避免作用在內(nèi)容不斷變動的圖層上凤覆,否則它緩存方面的好處就會消失链瓦,而且會讓性能變的更糟。

為了檢測你是否正確地使用了光柵化方式盯桦,用Instrument查看一下Color Hits Green和Misses Red項目猪半,是否已光柵化圖像被頻繁地刷新(這樣就說明圖層并不是光柵化的好選擇趾诗,或則你無意間觸發(fā)了不必要的改變導致了重繪行為)。

屏幕外渲染

當圖層屬性的混合體被指定為在未預合成之前不能直接在屏幕中繪制時,屏幕外渲染就被喚起了恨诱。屏幕外渲染并不意味著軟件繪制,但是它意味著圖層必須在被顯示之前在一個屏幕外上下文中被渲染(不論CPU還是GPU)抹沪。圖層的以下屬性將會觸發(fā)屏幕外繪制:

圓角(當和maskToBounds一起使用時)

圖層蒙板

陰影

屏幕外渲染和我們啟用光柵化時相似纵菌,除了它并沒有像光柵化圖層那么消耗大,子圖層并沒有被影響到玄柠,而且結(jié)果也沒有被緩存突梦,所以不會有長期的內(nèi)存占用。但是羽利,如果太多圖層在屏幕外渲染依然會影響到性能宫患。

有時候我們可以把那些需要屏幕外繪制的圖層開啟光柵化以作為一個優(yōu)化方式,前提是這些圖層并不會被頻繁地重繪这弧。

對于那些需要動畫而且要在屏幕外渲染的圖層來說娃闲,你可以用CAShapeLayer,contentsCenter或者shadowPath來獲得同樣的表現(xiàn)而且較少地影響到性能当宴。

CAShapeLayer

cornerRadius和maskToBounds獨立作用的時候都不會有太大的性能問題畜吊,但是當他倆結(jié)合在一起,就觸發(fā)了屏幕外渲染户矢。有時候你想顯示圓角并沿著圖層裁切子圖層的時候玲献,你可能會發(fā)現(xiàn)你并不需要沿著圓角裁切,這個情況下用CAShapeLayer就可以避免這個問題了梯浪。

你想要的只是圓角且沿著矩形邊界裁切捌年,同時還不希望引起性能問題。其實你可以用現(xiàn)成的UIBezierPath的構(gòu)造器+bezierPathWithRoundedRect:cornerRadius:(見清單15.1).這樣做并不會比直接用cornerRadius更快挂洛,但是它避免了性能問題礼预。

清單15.2 用CAShapeLayer畫一個圓角矩形

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19#import?"ViewController.h"

#import?@interface?ViewController?()

@property?(nonatomic,?weak)?IBOutlet?UIView?*layerView;

@end

@implementation?ViewController

-?(void)viewDidLoad

{

[superviewDidLoad];

//create?shape?layer

CAShapeLayer?*blueLayer?=?[CAShapeLayer?layer];

blueLayer.frame?=?CGRectMake(50,?50,?100,?100);

blueLayer.fillColor?=?[UIColor?blueColor].CGColor;

blueLayer.path?=?[UIBezierPath?bezierPathWithRoundedRect:

CGRectMake(0,?0,?100,?100)?cornerRadius:20].CGPath;

?

//add?it?to?our?view

[self.layerView.layer?addSublayer:blueLayer];

}

@end

可伸縮圖片

另一個創(chuàng)建圓角矩形的方法就是用一個圓形內(nèi)容圖片并結(jié)合第二章『寄宿圖』提到的contensCenter屬性去創(chuàng)建一個可伸縮圖片(見清單15.2).理論上來說,這個應該比用CAShapeLayer要快虏劲,因為一個可拉伸圖片只需要18個三角形(一個圖片是由一個3*3網(wǎng)格渲染而成)托酸,然而褒颈,許多都需要渲染成一個順滑的曲線。在實際應用上励堡,二者并沒有太大的區(qū)別谷丸。

清單15.2 用可伸縮圖片繪制圓角矩形

1

2

3

4

5

6

7

8

9

10

11

12

13

14@implementation?ViewController

-?(void)viewDidLoad

{

[superviewDidLoad];

//create?layer

CALayer?*blueLayer?=?[CALayer?layer];

blueLayer.frame?=?CGRectMake(50,?50,?100,?100);

blueLayer.contentsCenter?=?CGRectMake(0.5,?0.5,?0.0,?0.0);

blueLayer.contentsScale?=?[UIScreen?mainScreen].scale;

blueLayer.contents?=?(__bridge?id)[UIImage?imageNamed:@"Circle.png"].CGImage;

//add?it?to?our?view

[self.layerView.layer?addSublayer:blueLayer];

}

@end

使用可伸縮圖片的優(yōu)勢在于它可以繪制成任意邊框效果而不需要額外的性能消耗。舉個例子应结,可伸縮圖片甚至還可以顯示出矩形陰影的效果刨疼。

shadowPath

在第2章我們有提到shadowPath屬性。如果圖層是一個簡單幾何圖形如矩形或者圓角矩形(假設不包含任何透明部分或者子圖層)鹅龄,創(chuàng)建出一個對應形狀的陰影路徑就比較容易揩慕,而且Core Animation繪制這個陰影也相當簡單,避免了屏幕外的圖層部分的預排版需求扮休。這對性能來說很有幫助迎卤。

如果你的圖層是一個更復雜的圖形,生成正確的陰影路徑可能就比較難了玷坠,這樣子的話你可以考慮用繪圖軟件預先生成一個陰影背景圖止吐。

混合和過度繪制

在第12章有提到,GPU每一幀可以繪制的像素有一個最大限制(就是所謂的fill rate)侨糟,這個情況下可以輕易地繪制整個屏幕的所有像素碍扔。但是如果由于重疊圖層的關(guān)系需要不停地重繪同一區(qū)域的話,掉幀就可能發(fā)生了秕重。

GPU會放棄繪制那些完全被其他圖層遮擋的像素不同,但是要計算出一個圖層是否被遮擋也是相當復雜并且會消耗處理器資源。同樣溶耘,合并不同圖層的透明重疊像素(即混合)消耗的資源也是相當客觀的二拐。所以為了加速處理進程,不到必須時刻不要使用透明圖層凳兵。任何情況下百新,你應該這樣做:

給視圖的backgroundColor屬性設置一個固定的,不透明的顏色

設置opaque屬性為YES

這樣做減少了混合行為(因為編譯器知道在圖層之后的東西都不會對最終的像素顏色產(chǎn)生影響)并且計算得到了加速庐扫,避免了過度繪制行為因為Core Animation可以舍棄所有被完全遮蓋住的圖層饭望,而不用每個像素都去計算一遍。

如果用到了圖像形庭,盡量避免透明除非非常必要铅辞。如果圖像要顯示在一個固定的背景顏色或是固定的背景圖之前,你沒必要相對前景移動萨醒,你只需要預填充背景圖片就可以避免運行時混色了斟珊。

如果是文本的話,一個白色背景的UILabel(或者其他顏色)會比透明背景要更高效富纸。

最后囤踩,明智地使用shouldRasterize屬性旨椒,可以將一個固定的圖層體系折疊成單張圖片,這樣就不需要每一幀重新合成了堵漱,也就不會有因為子圖層之間的混合和過度繪制的性能問題了钩乍。

減少圖層數(shù)量

初始化圖層,處理圖層怔锌,打包通過IPC發(fā)給渲染引擎,轉(zhuǎn)化成OpenGL幾何圖形变过,這些是一個圖層的大致資源開銷埃元。事實上,一次性能夠在屏幕上顯示的最大圖層數(shù)量也是有限的媚狰。

確切的限制數(shù)量取決于iOS設備岛杀,圖層類型,圖層內(nèi)容和屬性等崭孤。但是總得說來可以容納上百或上千個类嗤,下面我們將演示即使圖層本身并沒有做什么也會遇到的性能問題。

裁切

在對圖層做任何優(yōu)化之前辨宠,你需要確定你不是在創(chuàng)建一些不可見的圖層遗锣,圖層在以下幾種情況下回事不可見的:

圖層在屏幕邊界之外,或是在父圖層邊界之外嗤形。

完全在一個不透明圖層之后精偿。

完全透明

Core Animation非常擅長處理對視覺效果無意義的圖層。但是經(jīng)常性地赋兵,你自己的代碼會比Core Animation更早地想知道一個圖層是否是有用的笔咽。理想狀況下,在圖層對象在創(chuàng)建之前就想知道霹期,以避免創(chuàng)建和配置不必要圖層的額外工作叶组。

舉個例子。清單15.3 的代碼展示了一個簡單的滾動3D圖層矩陣历造。這看上去很酷甩十,尤其是圖層在移動的時候(見圖15.1),但是繪制他們并不是很麻煩吭产,因為這些圖層就是一些簡單的矩形色塊枣氧。

清單15.3 繪制3D圖層矩陣

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39#import?"ViewController.h"

#import?#define?WIDTH?10

#define?HEIGHT?10

#define?DEPTH?10

#define?SIZE?100

#define?SPACING?150

#define?CAMERA_DISTANCE?500

@interface?ViewController?()

@property?(nonatomic,?strong)?IBOutlet?UIScrollView?*scrollView;

@end

@implementation?ViewController

-?(void)viewDidLoad

{

[superviewDidLoad];

//set?content?size

self.scrollView.contentSize?=?CGSizeMake((WIDTH?-?1)*SPACING,?(HEIGHT?-?1)*SPACING);

//set?up?perspective?transform

CATransform3D?transform?=?CATransform3DIdentity;

transform.m34?=?-1.0?/?CAMERA_DISTANCE;

self.scrollView.layer.sublayerTransform?=?transform;

//create?layers

for(int?z?=?DEPTH?-?1;?z?>=?0;?z--)?{

for(int?y?=?0;?y?<?HEIGHT;?y++)?{

for(int?x?=?0;?x?<?WIDTH;?x++)?{

//create?layer

CALayer?*layer?=?[CALayer?layer];

layer.frame?=?CGRectMake(0,?0,?SIZE,?SIZE);

layer.position?=?CGPointMake(x*SPACING,?y*SPACING);

layer.zPosition?=?-z*SPACING;

//set?background?color

layer.backgroundColor?=?[UIColor?colorWithWhite:1-z*(1.0/DEPTH)?alpha:1].CGColor;

//attach?to?scroll?view

[self.scrollView.layer?addSublayer:layer];

}

}

}

//log

NSLog(@"displayed:?%i",?DEPTH*HEIGHT*WIDTH);?}

@end

圖15.1 滾動的3D圖層矩陣

WIDTH,HEIGHT和DEPTH常量控制著圖層的生成垮刹。在這個情況下达吞,我們得到的是10*10*10個圖層,總量為1000個荒典,不過一次性顯示在屏幕上的大約就幾百個酪劫。

如果把WIDTH和HEIGHT常量增加到100吞鸭,我們的程序就會慢得像龜爬了。這樣我們有了100000個圖層覆糟,性能下降一點兒也不奇怪刻剥。

但是顯示在屏幕上的圖層數(shù)量并沒有增加,那么根本沒有額外的東西需要繪制滩字。程序慢下來的原因其實是因為在管理這些圖層上花掉了不少功夫造虏。他們大部分對渲染的最終結(jié)果沒有貢獻,但是在丟棄這么圖層之前麦箍,Core Animation要強制計算每個圖層的位置漓藕,就這樣,我們的幀率就慢了下來挟裂。

我們的圖層是被安排在一個均勻的柵格中享钞,我們可以計算出哪些圖層會被最終顯示在屏幕上,根本不需要對每個圖層的位置進行計算诀蓉。這個計算并不簡單栗竖,因為我們還要考慮到透視的問題。如果我們直接這樣做了渠啤,Core Animation就不用費神了狐肢。

既然這樣,讓我們來重構(gòu)我們的代碼吧沥曹。改造后处坪,隨著視圖的滾動動態(tài)地實例化圖層而不是事先都分配好。這樣架专,在創(chuàng)造他們之前同窘,我們就可以計算出是否需要他。接著部脚,我們增加一些代碼去計算可視區(qū)域這樣就可以排除區(qū)域之外的圖層了想邦。清單15.4是改造后的結(jié)果。

清單15.4 排除可視區(qū)域之外的圖層

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77#import?"ViewController.h"

#import?#define?WIDTH?100

#define?HEIGHT?100

#define?DEPTH?10

#define?SIZE?100

#define?SPACING?150

#define?CAMERA_DISTANCE?500

#define?PERSPECTIVE(z)?(float)CAMERA_DISTANCE/(z?+?CAMERA_DISTANCE)

@interface?ViewController?()?@property?(nonatomic,?weak)?IBOutlet?UIScrollView?*scrollView;

@end

@implementation?ViewController

-?(void)viewDidLoad

{

[superviewDidLoad];

//set?content?size

self.scrollView.contentSize?=?CGSizeMake((WIDTH?-?1)*SPACING,?(HEIGHT?-?1)*SPACING);

//set?up?perspective?transform

CATransform3D?transform?=?CATransform3DIdentity;

transform.m34?=?-1.0?/?CAMERA_DISTANCE;

self.scrollView.layer.sublayerTransform?=?transform;

}

?

-?(void)viewDidLayoutSubviews

{

[self?updateLayers];

}

-?(void)scrollViewDidScroll:(UIScrollView?*)scrollView

{

[self?updateLayers];

}

-?(void)updateLayers

{

//calculate?clipping?bounds

CGRect?bounds?=?self.scrollView.bounds;

bounds.origin?=?self.scrollView.contentOffset;

bounds?=?CGRectInset(bounds,?-SIZE/2,?-SIZE/2);

//create?layers

NSMutableArray?*visibleLayers?=?[NSMutableArray?array];

for(int?z?=?DEPTH?-?1;?z?>=?0;?z--)

{

//increase?bounds?size?to?compensate?for?perspective

CGRect?adjusted?=?bounds;

adjusted.size.width?/=?PERSPECTIVE(z*SPACING);

adjusted.size.height?/=?PERSPECTIVE(z*SPACING);

adjusted.origin.x?-=?(adjusted.size.width?-?bounds.size.width)?/?2;

adjusted.origin.y?-=?(adjusted.size.height?-?bounds.size.height)?/?2;

for(int?y?=?0;?y?<?HEIGHT;?y++)?{

//check?if?vertically?outside?visible?rect

if(y*SPACING?<?adjusted.origin.y?||?y*SPACING?>=?adjusted.origin.y?+?adjusted.size.height)

{

continue;

}

for(int?x?=?0;?x?<?WIDTH;?x++)?{

//check?if?horizontally?outside?visible?rect

if(x*SPACING?<?adjusted.origin.x?||x*SPACING?>=?adjusted.origin.x?+?adjusted.size.width)

{

continue;

}

?

//create?layer

CALayer?*layer?=?[CALayer?layer];

layer.frame?=?CGRectMake(0,?0,?SIZE,?SIZE);

layer.position?=?CGPointMake(x*SPACING,?y*SPACING);

layer.zPosition?=?-z*SPACING;

//set?background?color

layer.backgroundColor?=?[UIColor?colorWithWhite:1-z*(1.0/DEPTH)?alpha:1].CGColor;

//attach?to?scroll?view

[visibleLayers?addObject:layer];

}

}

}

//update?layers

self.scrollView.layer.sublayers?=?visibleLayers;

//log

NSLog(@"displayed:?%i/%i",?[visibleLayers?count],?DEPTH*HEIGHT*WIDTH);

}

@end

這個計算機制并不具有普適性委刘,但是原則上是一樣丧没。(當你用一個UITableView或者UICollectionView時,系統(tǒng)做了類似的事情)锡移。這樣做的結(jié)果呕童?我們的程序可以處理成百上千個『虛擬』圖層而且完全沒有性能問題!因為它不需要一次性實例化幾百個圖層淆珊。

對象回收

處理巨大數(shù)量的相似視圖或圖層時還有一個技巧就是回收他們夺饲。對象回收在iOS頗為常見;UITableView和UICollectionView都有用到,MKMapView中的動畫pin碼也有用到往声,還有其他很多例子擂找。

對象回收的基礎(chǔ)原則就是你需要創(chuàng)建一個相似對象池。當一個對象的指定實例(本例子中指的是圖層)結(jié)束了使命浩销,你把它添加到對象池中贯涎。每次當你需要一個實例時,你就從池中取出一個慢洋。當且僅當池中為空時再創(chuàng)建一個新的塘雳。

這樣做的好處在于避免了不斷創(chuàng)建和釋放對象(相當消耗資源,因為涉及到內(nèi)存的分配和銷毀)而且也不必給相似實例重復賦值普筹。

好了败明,讓我們再次更新代碼吧(見清單15.5)

清單15.5 通過回收減少不必要的分配

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82@interface?ViewController?()?@property?(nonatomic,?weak)?IBOutlet?UIScrollView?*scrollView;

@property?(nonatomic,?strong)?NSMutableSet?*recyclePool;

@end

@implementation?ViewController

-?(void)viewDidLoad

{

[superviewDidLoad];//create?recycle?pool

self.recyclePool?=?[NSMutableSet?set];

//set?content?size

self.scrollView.contentSize?=?CGSizeMake((WIDTH?-?1)*SPACING,?(HEIGHT?-?1)*SPACING);

//set?up?perspective?transform

CATransform3D?transform?=?CATransform3DIdentity;

transform.m34?=?-1.0?/?CAMERA_DISTANCE;

self.scrollView.layer.sublayerTransform?=?transform;

}

-?(void)viewDidLayoutSubviews

{

[self?updateLayers];

}

-?(void)scrollViewDidScroll:(UIScrollView?*)scrollView

{

[self?updateLayers];

}

-?(void)updateLayers?{

?

//calculate?clipping?bounds

CGRect?bounds?=?self.scrollView.bounds;

bounds.origin?=?self.scrollView.contentOffset;

bounds?=?CGRectInset(bounds,?-SIZE/2,?-SIZE/2);

//add?existing?layers?to?pool

[self.recyclePool?addObjectsFromArray:self.scrollView.layer.sublayers];

//disable?animation

[CATransaction?begin];

[CATransaction?setDisableActions:YES];

//create?layers

NSInteger?recycled?=?0;

NSMutableArray?*visibleLayers?=?[NSMutableArray?array];

for(int?z?=?DEPTH?-?1;?z?>=?0;?z--)

{

//increase?bounds?size?to?compensate?for?perspective

CGRect?adjusted?=?bounds;

adjusted.size.width?/=?PERSPECTIVE(z*SPACING);

adjusted.size.height?/=?PERSPECTIVE(z*SPACING);

adjusted.origin.x?-=?(adjusted.size.width?-?bounds.size.width)?/?2;?adjusted.origin.y?-=?(adjusted.size.height?-?bounds.size.height)?/?2;

for(int?y?=?0;?y?<?HEIGHT;?y++)?{

//check?if?vertically?outside?visible?rect

if(y*SPACING?<?adjusted.origin.y?||

y*SPACING?>=?adjusted.origin.y?+?adjusted.size.height)

{

continue;

}

for(int?x?=?0;?x?<?WIDTH;?x++)?{

//check?if?horizontally?outside?visible?rect

if(x*SPACING?<?adjusted.origin.x?||

x*SPACING?>=?adjusted.origin.x?+?adjusted.size.width)

{

continue;

}

//recycle?layer?if?available

CALayer?*layer?=?[self.recyclePool?anyObject];if(layer)

{

?

recycled?++;

[self.recyclePool?removeObject:layer];?}

else

{

layer.frame?=?CGRectMake(0,?0,?SIZE,?SIZE);?}

//set?position

layer.position?=?CGPointMake(x*SPACING,?y*SPACING);?layer.zPosition?=?-z*SPACING;

//set?background?color

layer.backgroundColor?=

[UIColor?colorWithWhite:1-z*(1.0/DEPTH)?alpha:1].CGColor;

//attach?to?scroll?view

[visibleLayers?addObject:layer];?}

}?}

[CATransaction?commit];//update?layers

self.scrollView.layer.sublayers?=?visibleLayers;

//log

NSLog(@"displayed:?%i/%i?recycled:?%i",

[visibleLayers?count],?DEPTH*HEIGHT*WIDTH,?recycled);

}

@end

本例中,我們只有圖層對象這一種類型斑芜,但是UIKit有時候用一個標識符字符串來區(qū)分存儲在不同對象池中的不同的可回收對象類型。

你可能注意到當設置圖層屬性時我們用了一個CATransaction來抑制動畫效果祟霍。在之前并不需要這樣做杏头,因為在顯示之前我們給所有圖層設置一次屬性。但是既然圖層正在被回收沸呐,禁止隱式動畫就有必要了醇王,不然當屬性值改變時,圖層的隱式動畫就會被觸發(fā)崭添。

Core Graphics繪制

當排除掉對屏幕顯示沒有任何貢獻的圖層或者視圖之后寓娩,長遠看來,你可能仍然需要減少圖層的數(shù)量呼渣。例如棘伴,如果你正在使用多個UILabel或者UIImageView實例去顯示固定內(nèi)容,你可以把他們?nèi)刻鎿Q成一個單獨的視圖屁置,然后用-drawRect:方法繪制出那些復雜的視圖層級焊夸。

這個提議看上去并不合理因為大家都知道軟件繪制行為要比GPU合成要慢而且還需要更多的內(nèi)存空間,但是在因為圖層數(shù)量而使得性能受限的情況下蓝角,軟件繪制很可能提高性能呢阱穗,因為它避免了圖層分配和操作問題。

你可以自己實驗一下這個情況使鹅,它包含了性能和柵格化的權(quán)衡揪阶,但是意味著你可以從圖層樹上去掉子圖層(用shouldRasterize,與完全遮擋圖層相反)患朱。

-renderInContext: 方法

用Core Graphics去繪制一個靜態(tài)布局有時候會比用層級的UIView實例來得快鲁僚,但是使用UIView實例要簡單得多而且比用手寫代碼寫出相同效果要可靠得多,更邊說Interface Builder來得直接明了。為了性能而舍棄這些便利實在是不應該蕴茴。

幸好劝评,你不必這樣,如果大量的視圖或者圖層真的關(guān)聯(lián)到了屏幕上將會是一個大問題倦淀。沒有與圖層樹相關(guān)聯(lián)的圖層不會被送到渲染引擎蒋畜,也沒有性能問題(在他們被創(chuàng)建和配置之后)。

使用CALayer的-renderInContext:方法撞叽,你可以將圖層及其子圖層快照進一個Core Graphics上下文然后得到一個圖片姻成,它可以直接顯示在UIImageView中,或者作為另一個圖層的contents愿棋。不同于shouldRasterize —— 要求圖層與圖層樹相關(guān)聯(lián) —— 科展,這個方法沒有持續(xù)的性能消耗。

當圖層內(nèi)容改變時糠雨,刷新這張圖片的機會取決于你(不同于shouldRasterize才睹,它自動地處理緩存和緩存驗證),但是一旦圖片被生成甘邀,相比于讓Core Animation處理一個復雜的圖層樹琅攘,你節(jié)省了相當客觀的性能。

總結(jié)

本章學習了使用Core Animation圖層可能遇到的性能瓶頸松邪,并討論了如何避免或減小壓力坞琴。你學習了如何管理包含上千虛擬圖層的場景(事實上只創(chuàng)建了幾百個)。同時也學習了一些有用的技巧逗抑,選擇性地選取光柵化或者繪制圖層內(nèi)容在合適的時候重新分配給CPU和GPU剧辐。這些就是我們要講的關(guān)于Core Animation的全部了(至少可以等到蘋果發(fā)明什么新的玩意兒)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市邮府,隨后出現(xiàn)的幾起案子荧关,更是在濱河造成了極大的恐慌,老刑警劉巖褂傀,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件羞酗,死亡現(xiàn)場離奇詭異,居然都是意外死亡紊服,警方通過查閱死者的電腦和手機檀轨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來欺嗤,“玉大人参萄,你說我怎么就攤上這事〖灞” “怎么了讹挎?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我筒溃,道長马篮,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任怜奖,我火速辦了婚禮浑测,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘歪玲。我一直安慰自己迁央,他們只是感情好,可當我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布滥崩。 她就那樣靜靜地躺著岖圈,像睡著了一般。 火紅的嫁衣襯著肌膚如雪钙皮。 梳的紋絲不亂的頭發(fā)上蜂科,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天,我揣著相機與錄音短条,去河邊找鬼导匣。 笑死,一個胖子當著我的面吹牛慌烧,可吹牛的內(nèi)容都是我干的逐抑。 我是一名探鬼主播鸠儿,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼屹蚊,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了进每?” 一聲冷哼從身側(cè)響起汹粤,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎田晚,沒想到半個月后嘱兼,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡贤徒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年芹壕,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片接奈。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡踢涌,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出序宦,到底是詐尸還是另有隱情睁壁,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站潘明,受9級特大地震影響行剂,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜钳降,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一厚宰、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧牲阁,春花似錦固阁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至凌唬,卻和暖如春并齐,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背客税。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工况褪, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人更耻。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓测垛,卻偏偏與公主長得像,于是被迫代替她去往敵國和親秧均。 傳聞我的和親對象是個殘疾皇子食侮,可洞房花燭夜當晚...
    茶點故事閱讀 45,037評論 2 355

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