ios 性能优化
来源:互联网 发布:java中的各种buffer 编辑:程序博客网 时间:2024/05/24 03:41
核心动画在设计的时候就考虑了性能。
它首先是层级别的呈现,并且设计运行在小型的设备上(iphone和itouch),这些设备内存有限,并且cpu和gpu不如桌面电脑上的强大,核心动画是被设计的比较高效的,但是并不意味着你就可以在代码中随便用。
任何复杂的系统都会考虑性能的问题。幸运的是核心动画在处理复杂动画时,已经帮你处理的很多性能问题,你也需要改善一下代
码让基于核心动画的应用程序具有更好的性能提升。
这一章展示给你的是,如何来充分利用核心动画。
本章的开始会有一些指南。这些都是你应该记住的,当你做核心动画的程序时,你应该通过这些指南精炼你的代码。然后你会了解
到如何平衡GPU,怎么用多线程的动画,并且使用CATiledLayer呈现大的图像给用户,使你的应用程序不至于图像太大而陷入困
境。
硬件加速
核心动画的一个重要的好处就是它利用了mac的硬件的优点。
先前,开发者需要在应用上做动画时,动画产生需要在CPU上,并且会耗掉cpu的循环,这样就会使程序慢。制作用户界面动画会消
耗大量的时间,通常还要涉及到OpenGL或其他的一些技术与图形处理单元(GPU)的工作。使用核心动画,你会能够自由的使用
硬件加速。你仅仅需要做的就是定义一个动画,开启它,让它运行。你不必担心装载到GPU中,因为核心动画都帮你自动处理了。
当您在设计用户界面时,要牢记。那些你认为是一个复杂的动画,但是放到GPU上执行可能微不足道。 GPU是专门来做屏幕上矩形
的移动,三维空间转化等。只有当GPU的限制产生时,我们开始看到性能下降。
最小原则
下面的段落所呈现的原则,都是你需要在做核心动画时,需要牢记的规则。避免幕后渲染
不管你是在桌面应用程序还是在touch设备上,你需要限制你的绘制,仅仅当这些区域对于用户可见时再绘制。幸运的是,-
drawRect:方法只有当矩形区域为脏时,才会传递过来,因此你可以精确的控制每个循环的绘画次数。
限制滤镜和阴影
滤镜和阴影需要耗费大量的时间来计算和渲染在核心动画上。因而,建议尽量保持小并且有可能的话,避免上述的情况。这有一些技巧:
尤其是滤镜的情况,它可能要渲染一个静态的,临时的图像。在动画假设被渲染时,这个静态的图像可能被使用来代替需要动画渲
染的层。当动画完成时,渲染的层就被交换到了后面。虽然这不能被应用到所有的情况,这也会产生性能的优化。这些动作可以通
过在最上层调用-drawInContext来创建这个用来交换的静态图片。
阴影也是代价很高的。
因为他们属性部分透明的层,它需要大量的计算,来决定每个像素,因为每个像素都需要计算,直到有不透明的层遇到。
如果阴影重叠的话,就增加了消耗。考虑限制只有最外层的阴影,并允许内层不产生任何阴影。参见“最小alpha混合”在本章的后
面。
明智的使用变换
转换提供了一个很好的方式,给用户一个视觉感受,应用程序怎么改变并且变成了什么样。从窗口滑出或者滑入的方式,给用户了
一个明显的视觉感触。然而例如要转换滤镜和阴影话,这是对性能的一个很大考验。
因而,在设计应用程序时,你应该考虑在可视层中有多少转换或者它们应该转换多快。
避免嵌套转换
作为核心动画的强大之处,可以彼此同时转换多个层。例如,你可以在一些层中转换层中所有z轴。然而,要注意到这些转换都是实
时执行的,没有缓存。因而,当你移动或者让层做动画时,这个层有了很多层的转换,每个转换都需要动画重新计算它们的帧。
为了增加性能,避免使用多层级的转换,当动画运行时。
限制透明度的混合
前面讨论了嵌套转换,透明度混合也是实时计算的。当一个层是部分透明时,透明部分的动画就要在动画帧中重新计算。记住这
些,当动画层中有透明时,尽量减少计算。滑动层之后的层是透明的会导致极大的性能消耗。
幸运的是,有个方法可以找到那个层有透明度混合,从而移除它。对于iphone这种有限的资源它是非常有用的。来核对透明度混
合,启动iphone下的应用程序,使用instruments。在里面增加核心动画instrument。
当你的应用程序在iphone上运行时并且你也附带instrument到程序上,那么转换到核心动画的instruments上。
只要运行,你立马就可以看到效果。整个iphone程序被分成红色和绿色。事实上,这里没用应用程序运行,你可以看到iphone主屏
幕的效果。
当可用时,不同的颜色混合就会展示不同的颜色,因此你能快速的定位到应用程序那些有问题。绿色罩住的区域是没有颜色混合
的,而红色的是有的。目标就是来减少红色区域。例如,在TransparentCocoaTouch应用程序中,你可以看到所有的UILabel对象都
有一个透明背景。
当你检查你的代码时,你就会看到那些区域是有问题的。这里看-initWithFrame:reuseIdentifier:中的CustomTableViewCell对象,问
题就在这里。
-(id)initWithFrame:(CGRect)frame reuseIdentifier:(NSString*)ident { if (!(self = [superinitWithFrame:frame reuseIdentifier:ident])) return nil; UIView *contentView = [self contentView]; imageView = [[UIImageViewalloc] initWithFrame:CGRectMake(5, 5, 64, 64)]; [imageView setContentMode:UIViewContentModeScaleAspectFit]; [contentView addSubview:imageView]; [imageView release]; titleLabel = [[UILabel alloc] initWithFrame:CGRectZero]; [titleLabel setFont:[UIFontboldSystemFontOfSize:14.0f]]; [titleLabel setBackgroundColor:[UIColor clearColor]]; [contentView addSubview:titleLabel]; [titleLabel release]; descriptionLabel = [[UILabelalloc] initWithFrame:CGRectZero]; [descriptionLabel setNumberOfLines:0]; [descriptionLabel setFont:[UIFont systemFontOfSize:6.0f]]; [descriptionLabel setBackgroundColor:[UIColor clearColor]]; [contentView addSubview:descriptionLabel]; [descriptionLabel release]; [[NSNotificationCenterdefaultCenter] addObserver:self selector:@selector(imageUpdated:) name:kImageDownloadComplete object:nil]; return self; }
就像你看到的,UILabel对象,titleLabel和descriptionLabel,两个的背景颜色都是被设定为[UIColor clearColor],引起了颜色混合的
发生。为了纠正这些,改变UIColor和整个UITableViewCell的背景颜色一样。这就减少了透明度的混合,提高了性能。
- (id)initWithFrame:(CGRect)framereuseIdentifier:(NSString*)ident { if (!(self = [superinitWithFrame:frame reuseIdentifier:ident])) return nil; UIView *contentView = [self contentView]; imageView = [[UIImageViewalloc] initWithFrame:CGRectMake(5, 5, 64, 64)]; [imageView setContentMode:UIViewContentModeScaleAspectFit]; [contentView addSubview:imageView]; [imageView release]; titleLabel = [[UILabel alloc] initWithFrame:CGRectZero]; [titleLabel setFont:[UIFontboldSystemFontOfSize:14.0f]]; [titleLabel setBackgroundColor:[UIColor whiteColor]]; [contentViewa ddSubview:titleLabel]; [titleLabel release]; descriptionLabel = [[UILabelalloc] initWithFrame:CGRectZero]; [descriptionLabel setNumberOfLines:0]; [descriptionLabel setFont:[UIFont systemFontOfSize:6.0f]]; [descriptionLabel setBackgroundColor:[UIColor whiteColor]]; [contentView addSubview:descriptionLabel]; [descriptionLabel release]; [[NSNotificationCenterdefaultCenter] addObserver:self selector:@selector(imageUpdated:) name:kImageDownloadComplete object:nil]; return self; }
平铺层
核心动画有一个强大的功能,使您可以快速查看应用程序的详细图像。CATileLayer是被设计成来展示大图像,但是不用把整个图像
导入到内存中,因而引起了性能的提升。
为了演示CATiledLayer这个有用的特征,打开TiledLayers示例应用程序。在这个应用程序中一个很大的图像(6064*4128)是被导入并
且还具有放大缩小的能力。正常情况下,一个很大的图像是被导入到内存中,这样会引起性能问题。然而,通过导入图像到
CATiledLayer中,核心动画来控制所有的内存问题。核心动画会装载图像需要展示的一部分而非整个图像。你需要配置的就是在
CATileLayer上给它一些细节的东西,就是根据尺寸来指定缩放的等级。
在TiledLayer应用程序中,一个简单的CATileLayer是在主窗口中初始化并且分配了一个NSSegmentedControl来管理缩放等级。
- (void)awakeFromNib { NSString *imagePath = [[NSBundle mainBundle] pathForResource:@”BigSpaceImage” ofType:@”png”]; NSData *data = [NSDatadataWithContentsOfFile:imagePath]; image = [[CIImageimageWithData:data] retain]; tiledLayer = [CATiledLayer layer]; [tiledLayer setBounds:CGRectMake(0, 0, 6064, 4128)]; float midX =NSMidX(NSRectFromCGRect([[view layer] frame])); float midY =NSMidY(NSRectFromCGRect([[view layer] frame])); [tiledLayer setPosition:CGPointMake(midX, midY)]; [tiledLayer setLevelsOfDetail:4]; // number of levels [tiledLayersetTileSize:CGSizeMake(256, 256)]; [tiledLayer setDelegate:self]; [[view layer] addSublayer:tiledLayer]; }
当应用程序获得图像的路径时,那个路径通过CIImage的调用被导入然后存储起来用。CATileLayer是被初始化然后初始化将要展示
的图像边框。下面就定位图像的位置并且配置可用缩放的等级。最后一步就是吧AppDelegate作为代理分配给该层,以便于接收绘图
事件。
NSSegmentedControl是绑定在-zoom:方法上,每当segmented control改变状态时就发送事件。选择段的位置是被使用来决定图像
目前的缩放比例,在CATileLayer上展示的图像。这些可以通过它的sublayerTransform的CATransform3DMakeScale调用来实现,在
段控制器上传递一个x和y的值。
- (IBAction)zoom:(id)sender { CGFloat zoom = 1.0 / ([senderselectedSegment] + 1); [[view layer] setSublayerTransform:CATransform3DMakeScale(zoom, zoom, 1.0)]; }
最后你要实现的方法就是CATiledLayer的一个代理。这个方法可以使我们重载-drawInContext:方法,而不用继承CALayer。在默认
的-drawInContext:方法实现中,它会寻找一个代理,核对代理是否实现了-drawLayer:inContext:方法。如果这个情况为真,那么-
drawLayer:inContext就会被调用。
- (void)drawLayer:(CALayer*)layer inContext:(CGContextRef)context { [[CIContextc ontextWithCGContext:context options:nil] drawImage:imageatPoint:CGPointMake(0, 0) fromRect:CGRectMake(0, 0, 6064, 4128)]; }
这个方法使用了在-awakFromNib中初始化的图像,并且使用了图像尺寸的边框绘制了它到了图像上下文中。应该注意的是,你不需
要控制任何的缩放计算,变换等方法。CATileLayer都自动帮你控制了。
这些如何工作
CATiledLayer导入图像成快,名字就是由此而来。每个块都响应一个图像的细节,根据你设定的块的尺寸。当你要求某个图像的细
节CATileLayer会根据原始的图像,缩放到渴望的尺寸,并且在屏幕上渲染之前,分割它成不同的块。因为这些块都被CATiledlayer
缓存下来,所以你可以滚动图像,并且很快的改变图像的细节。
CATiledLayer会缓存足够多的块,而当它也会根据需要扔掉不同的块。如果将来你再次需要他就会再次的生成。这意味着块的呈现
是一个异步的过程,那么用户就会看到延时,之后才会绘制到层上。一个很好的例子就是google地图的应用程序,如图12-5.如果你
缩放块的等级,图像不会立刻展示,而是有一些网格组成。当块装载完成时,就会代替网格显示出来。
多线程的动画
核心动画是线程安全的,因为它是安装多核系统的思想设计的。
这意味着你在多线程中操控核心动画,不会影响到其他的内部数据,这点非常的重要。然而,核心动画有一个地方不是线程安全
的,就是当它进入到CALayer的属性中时。
如果你的应用程序在主线程进入到属性中时,又在其他线程中进入到该属性,结果是不确定的。它可能的结果是,要么动画没有效
果,要么程序crash。当你要尝试获得一个属性,并且改变它时,你需要先存储属性先前的状态,并且这个行为要加锁,使这个过程
具有原子性。
[layer lock] float opacity = layer.opacity;layer.opacity = opacity + .1; [layer unlock];
在这个例子中,[CATransactionlock]用来防止任何其他线程获得锁,会有效的阻碍其他线程。这种序列话的进入到属性中,保证在我
们处理的过程中,属性不会被改变。
当需要层工作在多线程中,我们需要注意的如下:
获取一个锁时间太长的话,会让界面停止绘制。因为进入到层是被锁住了,不能在其他线程中进入,系统的其他部分也不能进入直
到这个锁被释放了。记住任何时候你锁住了一个层,就要尽快的解锁。
第二个问题是所有线程都遇到的问题,不仅仅是核心动画,那就是死锁。如果你锁住了一个层在一个线程中,这个层需要进入另一
个属性,但是这个属性是被另一个线程锁住,那么整个应用程序就会无响应,结果就是程序的crash。
滤镜的多线程
不像核心动画,核心图像滤镜不是线程安全的。这以为这你应该仅仅在单线程中处理核心图像。然而,也可以通过键值对(KVC)和关
键路径在多线程中控制滤镜。例如,如果你有一个层滤镜名字是acme,并且有一个属性叫做job,你可以通过下面的代码进行调整
[layer setvalue:myValue forKeyPath:@”layer.filters.acme.job”];
这将保证改变是原子的,因而是线程安全的。
线程和运行循环
当核心动画在多线程中工作时,你需要意识到的是在任何线程上调用-setNeedsDisplay,都会给层一个-display的消息。如果标记一
个层需要显示,而非在主线程中,那么你就必须等待一个运行循环去确保线程已经运行了-display的方法。如果不这样,-display就从
来不被调用,因为线程已经终结了,会引起一些异常的效果。
尽管它是可能在线程中调用-setNeedsDisplay,而不在主线程中。但是这对绘制工作影响很小的,但多数情况下建议使用-
performSelectorOnMainThread:方法,让它在主线程中运行。
总结
这一章,我们看到了关于性能方面的一些建议和策略。但是在处理性能之前,建议先完成代码。不要试着花费大量的时间来优化代
码,除非性能的问题已经发生。
这里也建议通过一些记录结果来测试一些潜在的瓶颈。有时候,一个简答的fps(每秒的帧率)记录就可以测试出来瓶颈。利用这些记
录,我们就能确定那些性能改变是正确的,代替盲目的找到一些无关紧要的因素。
- iOS内存优化,性能优化
- iphone ios 性能优化
- iOS性能优化系列
- ios 性能优化
- IOS性能优化
- iOS性能优化策略
- iOS性能优化系列
- iOS App性能优化
- iOS App性能优化
- iOS App性能优化
- iOS程序性能优化
- iOS性能优化策略
- ios 性能优化
- iOS性能优化小结
- iOS App性能优化
- iOS程序性能优化
- iOS App性能优化
- iOS性能优化系列
- jquery ajax 在submit按钮的click处理中应注意的地方
- hdu 2138 判断素数(Miller-Rabin算法)
- 教你如何迅速秒杀掉:99%的海量数据处理面试题
- hdu3400(三分套三分)
- MS 电面总结
- ios 性能优化
- 思考方式--SMART原则
- 不重启tomcat、jboss更新jsp,实时生效配置指导
- javascript函数执行写法
- LDR指令详解
- SeaBIOS study (1)
- [杭电]Max Sum
- UVa 592 - Island of Logic
- maven setting.xml说明