我的编程空间,编程开发者的网络收藏夹
学习永远不晚

iOS如何实现图形性能优化

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

iOS如何实现图形性能优化

这篇文章将为大家详细讲解有关iOS如何实现图形性能优化,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。

引言

当一个产品渐渐成熟,我们便开始重视产品性能的优化。而这其中图形性能的优化在iOS客户端占比较重要的部分。

这里我们将介绍Core Animation的运行机制,首先我们不要被它的名字误导了,Core Animation不是只用来做动画的,iOS视图的显示都是通过它来完成的,所以我们想要优化图形性能必须了解Core Animation。

下面我们根据苹果WWDC视频讲解来认识Core Animation工作机制,据此分析具体卡顿的原因,如何避免这些问题造成的卡顿,并且结合实际情况说明从哪些方面优化可以事半功倍。

Core Animation 工作机制

iOS如何实现图形性能优化

如上图所示,Core Animation在App将图层数据提交到应用外进程Render Server,这是Core Animation的服务端,把数据解码成GPU可执行的指令交给GPU执行。

可以看出一个问题渲染服务并不是在App进程内进行的,也就是说渲染部分我们无法进行优化,我们可以优化的点只能在第一个提交事务的阶段。那么这个阶段Core Animation到底做了什么呢?下面我们一起来看看!

Commit Transaction

提交事务分为四个阶段:布局、显示、准备、提交。

iOS如何实现图形性能优化

  • 布局阶段

    当调用addSubview时layer被加入到layer tree中,layoutSubviews被调用,创建view。同时还会进行数据查找,例如app做了本地化,label要显示这些本地化字符串必须从本地化文件中查找到对应语言的布局,这就涉及了I/O操作。所以这里主要是CPU工作,而瓶颈也会是CPU。

  • 显示阶段

    在这个阶段如果你重写了drawRect方法,Core Graphics会进行绘制渲染工作。为视图绘制寄宿图即contents。但是drawRect里绘制的内容不会立即显示出来,而是先备换窜起来,等需要的时候被更新到屏幕上。如手动调用setNeedsDisplay或sizeThatFits被调用,也可以设置cententMode属性值为UIViewContentModeRedraw当每次bounds改变会自动调用setNeedsDisplay方法。这个阶段主要是CPU和内存的消耗,很多人喜欢用Core Graphics的方法来绘制图形,认为可以提高性能,后面我们会说明这个方法的弊端。

  • 准备阶段

    这里的工作主要是图片的解码,因为大部分都是编码后的图片,要读取原始数据必须经过编码过程。并且当我们使用了iOS不支持的图片格式,即不支持硬编码,就需要进行转化工作,也是比较耗时的。所以这里就是GPU消耗,如果进行软解码也要消耗CPU。

  • 提交阶段

    最后一个阶段负责打包图层数据并发送到我们上面说的渲染服务中。这个过程是一个递归操作,图层树越复杂越是需要消耗更多资源。像CALaler有很多隐式动画属性也会在这里提交,省去了多次动画属性进程间的交互,提高了性能。

优化

根据上面我们所提到4个阶段,我们看看哪些因素会影响到App的性能,并且如何优化可以提高我们App的性能。

混合

平时我们写代码的时候,往往会给不同的CALayer添加不同的颜色,不同的透明度,我们最后看到是所有这些层CALayer混合出的结果。

那么在iOS中是如何进行混合的?前面我们说明了每个像素都包含了R(红)、G(绿)、B(蓝)和R(透明度),GPU要计算每个像素混合来的RGB值。那么如何计算这些颜色的混合值呢?假设在正常混合模式下,并且是像素对齐的两个CALayer,混合计算公式如下:

R = S + D * ( 1 – Sa )

苹果的文档中有对每个参数的解释:

The blend mode constants introduced in OS X v10.5   represent the Porter-Duff blend modes. The symbols in the   equations for these blend modes are:

    * R is the premultiplied result

    * S is the source color, and includes alpha

    * D is the destination color, and includes alpha

    * Ra, Sa, and Da are the alpha components of R, S, and D

R就是得到的结果色,S和D是包含透明度的源色和目标色,其实就是预先乘以透明度后的值。Sa就是源色的透明度。iOS为我们提供了多种的Blend mode:

 

kCGBlendModeClear,                  
kCGBlendModeCopy,                   
kCGBlendModeSourceIn,               
kCGBlendModeSourceOut,              
kCGBlendModeSourceAtop,             
kCGBlendModeDestinationOver,        
kCGBlendModeDestinationIn,          
kCGBlendModeDestinationOut,         
kCGBlendModeDestinationAtop,        
kCGBlendModeXOR,                    
kCGBlendModePlusDarker,             
kCGBlendModePlusLighter             

似乎计算也不是很复杂,但是这只是一个像素覆盖另一个像素简单的一步计算,而正常情况我们现实的界面会有非常多的层,每一层都会有百万计的像素,这都要GPU去计算,负担是很重的。

像素对齐

像素对齐就是视图上像素和屏幕上的物理像素完美对齐。上面我们说混合的时候,假设的情况是多个layer是在每个像素都完全对齐的情况下来进行计算的,如果像素不对齐的情况下,GPU需要进行Anti-aliasing反抗锯齿计算,GPU的负担就会加重。像素对齐的情况下,我们只需要把所有layer上的单个像素进行混合计算即可。

那么什么原因造成像素不对齐?主要有两点:

  1. 图片大小和UIImageView大小不符合2倍3倍关系时,如一张12x12二倍,18x18三倍的图,UIimageView的size为6x6才符合像素对齐。

  2. 边缘像素不对齐,即起始坐标不是整数,可以使用CGRectIntegral()方法去除小数位。 这两点都有可能造成像素不对齐。如果想获得更好的图形性能,作为开发者要尽可能得避免这两种情况。

不透明

上面我们说过一个混合计算的公式:

R = S + D * ( 1 – Sa )

如果Sa值为1,也就是源色对应的像素不透明。那么得到R = S,这样就只需要拷贝最上层的layer,不需要再进行复杂的计算了。因为下面层的layer全部是可不见的,所以GPU无需进行混合计算了。

如何让GPU知道这个图像是不透明的呢?如果使用的是CALayer,那么要把opaque属性设置成YES(默认是NO)。而若只用的是UIView,opaque默认属性是YES。当GPU知道是不透明的时候,只会做简单的拷贝工作,避免了复杂的计算,大大减轻了GPU的工作量。

如果加载一个没有alpha通道的图片,opaque属性会自动设置为YES。但是如果是一个每个像素alpha值都为100%的图片,尽管此图不透明但是Core Animation依然会假定是否存在alpha值不为100%的像素。

解码

上一篇文章我们有说到,一般在Core Animation准备阶段,会对图片进行解码操作,即把压缩的图像解码成位图数据。这是一个很消耗CPU的事情。系统是在图片将要渲染到屏幕之前再进行解码,而且默认是在主线程中进行的。所以我们可以将解码放在子线程中进行,下面简单列举一种解码方式:

NSString *picPath = [[NSBundle mainBundle] pathForResource:@"tests" ofType:@"png"];
NSData *imageData = [NSData dataWithContentsOfFile:picPath];//读取未解码图片数据

CGImageSourceRef imageSourceRef = CGImageSourceCreateWithData((__bridge CFTypeRef)imageData, NULL);
CGImageRef imageRef = CGImageSourceCreateImageAtIndex(imageSourceRef, 0, (CFDictionaryRef)@{(id)kCGImageSourceShouldCache:@(NO)});
CFRelease(imageSourceRef);
size_t width = CGImageGetWidth(imageRef);//获取图片宽度
size_t height = CGImageGetHeight(imageRef);//获取图片高度
CGColorSpaceRef colorSpace = CGImageGetColorSpace(imageRef);

size_t bitsPerComponent = CGImageGetBitsPerComponent(imageRef);//每个颜色组件占的bit数
size_t bitsPerPixel = CGImageGetBitsPerPixel(imageRef);//每个像素占几bit
size_t bytesPerRow = CGImageGetBytesPerRow(imageRef);//位图数据每行占多少bit
CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);

CGDataProviderRef dataProvider = CGImageGetDataProvider(imageRef);
CFRelease(imageRef);
CFDataRef dataRef = CGDataProviderCopyData(dataProvider);//获得解码后数据
CGDataProviderRef newProvider = CGDataProviderCreateWithCFData(dataRef);
CFRelease(dataRef);

CGImageRef newImageRef = CGImageCreate(width, height, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpace, bitmapInfo, newProvider, NULL, false, kCGRenderingIntentDefault);
CFRelease(newProvider);

UIImage *image = [UIImage imageWithCGImage:newImageRef scale:2.0 orientation:UIImageOrientationUp];
CFRelease(newImageRef);

另外,在iOS7之后苹果提供了一个属性kCGImageSourceShouldCacheImmediately,在CGImageSourceCreateImageAtIndex方法中,设置kCGImageSourceShouldCacheImmediately为kCFBooleanTrue的话可以立刻开始解压缩,默认为kCFBooleanFalse。

当然也像AFNetworking 中使用void CGContextDrawImage(CGContextRef __nullable c, CGRect rect, CGImageRef __nullable image)方法也可以实现解码,具体实现不在此赘述。

字节对齐

我们前面说像素对齐时,简单介绍了字节对齐。那么到底什么是字节对齐?为什么要字节对齐?和我们优化图形性能有什么关系呢?

字节对齐是对基本数据类型的地址做了一些限制,即某种数据类型对象的地址必须是其值的整数倍。例如,处理器从内存中读取一个8个字节的数据,那么数据地址必须是8的整数倍。

对齐是为了提高读取的性能。因为处理器读取内存中的数据不是一个一个字节读取的,而是一块一块读取的一般叫做cache lines。如果一个不对齐的数据放在了2个数据块中,那么处理器可能要执行两次内存访问。当这种不对齐的数据非常多的时候,就会影响到读取性能了。这样可能会牺牲一些储存空间,但是对提升了内存的性能,对现代计算机来说是更好的选择。

在iOS中,如果这个图像的数据没有字节对齐,那么Core Animation会自动拷贝一份数据做对齐处理。这里我们可以提前做好字节对齐。

在方法CGBitmapContextCreate(void * __nullable data, size_t width, size_t height, size_t bitsPerComponent, size_t bytesPerRow, CGColorSpaceRef __nullable space, uint32_t bitmapInfo)中,有一个参数bytesPerRow,意思是指定要使用的位图每行内存的字节数,ARMv7架构的处理器的cache lines是32byte,A9处理器的是64byte,这里我们要使bytesPerRow为64的整数倍。

具体可以参考官方文档Quartz 2D Programming Guide和WWDC 2012 Session 238 "iOS App Performance: Graphics and Animations"。字节对齐,在一般情况下,感觉对性能的影响很小,没必要的情况不要过早优化。

离屏渲染

离屏渲染(Off-Screen Rendering)是指GPU在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作。离屏渲染是很消耗性能的,因为首先要创建屏幕外缓冲区,还要进行两次上下文环境切换。先切换到屏幕外环境,离屏渲染完成后再切换到当前屏幕,上下文的切换是很高昂的消耗。产生离屏渲染的原因就是这些图层不能直接绘制在屏幕上,必须进行预合成。

产生离屏渲染的情况大概有几种: 

  1. cornerRadius和masksToBounds(UIView中是clipToBounds)一起使用的时候,单独使用不会触发离屏渲染。cornerRadius只对背景色起作用,所以有contents的图层需要对其进行裁剪。

  2. 为图层设置mask(遮罩)。

  3. layer的allowsGroupOpacity属性为YES且opacity小于1.0,GroupOpacity是指子图层的透明度值不能大于父图层的。 

  4. 设置了shadow(阴影)。

上面这几种情况都是GPU的离屏渲染,还有一种特殊的CPU离屏渲染。只要实现Core Graphics绘制API会产生CPU的离屏渲染。因为它也不是直接绘制到屏幕上的,而且先创建屏幕外的缓存。

我们如何解决这几个产生离屏渲染的问题呢?首先,GroupOpacity对性能几乎没有影响,在此就不多说了。圆角是一个无法避免的,网上有很多例子是用Core Graphics绘制来代替系统圆角的,但是Core Graphics是一种软件绘制,利用的是CPU,性能上要差上不少

当然在CPU利用率不是很高的界面是个不错的选择,但是有时候某个界面可能需要CPU去做其他消耗很大的事情,如网络请求。这个时候时候在用Core Graphics绘制大量的圆角图形就有可能出现掉帧。

这种情况怎么办呢?最好的就是设计师直接提供圆角图像。还有一种折中的方法就是在混合图层,在原图层上覆盖一个你要的圆角形状的图层,中间需要显示的部分是透明的,覆盖的部分和周围背景一致。

对于shadow,如果图层是个简单的几何图形或者圆角图形,我们可以通过设置shadowPath来优化性能,能大幅提高性能。示例如下:

imageView.layer.shadowColor = [UIColor grayColor].CGColor;
imageView.layer.shadowOpacity = 1.0;
imageView.layer.shadowRadius = 2.0;
UIBezierPath *path = [UIBezierPath bezierPathWithRect:imageView.frame];
imageView.layer.shadowPath = path.CGPath;

我们还可以通过设置shouldRasterize属性值为YES来强制开启离屏渲染。其实就是光栅化(Rasterization)。

既然离屏渲染这么不好,为什么我们还要强制开启呢?当一个图像混合了多个图层,每次移动时,每一帧都要重新合成这些图层,十分消耗性能。

当我们开启光栅化后,会在首次产生一个位图缓存,当再次使用时候就会复用这个缓存。但是如果图层发生改变的时候就会重新产生位图缓存。

所以这个功能一般不能用于UITableViewCell中,cell的复用反而降低了性能。最好用于图层较多的静态内容的图形。而且产生的位图缓存的大小是有限制的,一般是2.5个屏幕尺寸。在100ms之内不使用这个缓存,缓存也会被删除。所以我们要根据使用场景而定。

Instruments

上面我们说了这么多性能相关的因素,那么我们怎么进行性能的测试,怎么知道哪些因素影响了图形性能?苹果很人性得为我们提供了一个测试工具Instruments。可以在Xcode->Open Develeper Tools->Instruments中找到,我们看到这里面有很多的测试工具,像大家可能常用的检测内存泄漏的Leaks,在这里我们就讨论下Core Animation这个工具的使用。

Core Animation工具用来监测Core Animation性能。提供可见的FPS值。并且提供几个选项来测量渲染性能,下面我们来说明每个选项的能: 

  • Color Blended Layers:这个选项如果勾选,你能看到哪个layer是透明的,GPU正在做混合计算。显示红色的就是透明的,绿色就是不透明的。

  • Color Hits Green and Misses Red:如果勾选这个选项,且当我们代码中有设置shouldRasterize为YES,那么红色代表没有复用离屏渲染的缓存,绿色则表示复用了缓存。我们当然希望能够复用。

  • Color Copied Images:按照官方的说法,当图片的颜色格式GPU不支持的时候,即不是32bit的颜色格式,Core Animation会 拷贝一份数据让CPU进行转化。例如从网络上下载了8bit的颜色格式的图片,则需要CPU进行转化,这个区域会显示成蓝色。还有一种情况会触发Core Animation的copy方法,就是字节不对齐的时候。

  • Color Misaligned Images:勾选此项,如果图片需要缩放则标记为黄色,如果没有像素对齐则标记为紫色。像素对齐我们已经在上面有所介绍。

  • Color Offscreen-Rendered Yellow:用来检测离屏渲染的,如果显示黄色,表示有离屏渲染。当然还要结合Color Hits Green and Misses Red来看,是否复用了缓存。

  • Color OpenGL Fast Path Blue:这个选项对那些使用OpenGL的图层才有用,像是GLKView或者 CAEAGLLayer,如果不显示蓝色则表示使用了CPU渲染,绘制在了屏幕外,显示蓝色表示正常。

  • Flash Updated Regions:当对图层重绘的时候回显示黄色,如果频繁发生则会影响性能。可以用增加缓存来增强性能。官方文档Improving Drawing Performance(https://developer.apple.com/library/archive/documentation/2DDrawing/Conceptual/DrawingPrintingiOS/DrawingTips/DrawingTips.html)有所说明。

关于“iOS如何实现图形性能优化”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

iOS如何实现图形性能优化

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

iOS如何实现图形性能优化

这篇文章将为大家详细讲解有关iOS如何实现图形性能优化,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。引言当一个产品渐渐成熟,我们便开始重视产品性能的优化。而这其中图形性能的优化在iOS客户端占比较重要的部
2023-06-05

Golang 技术性能优化中如何实现分布式性能优化?

如何实现 golang 分布式性能优化?并发编程: 利用 goroutine 并行执行任务。分布式锁: 使用互斥锁防止并发操作导致数据不一致。分布式缓存: 使用 memcached 减少对慢速存储的访问。消息队列: 使用 kafka 解耦任
Golang 技术性能优化中如何实现分布式性能优化?
2024-05-12

iOS 下的图片处理与性能优化详解

目录图片在计算机世界中怎样被存储和表示?常见的图片格式如何判断图片的格式?UIImageView 的性能瓶颈解决性能瓶颈:强制解码总结图片在计算机世界中怎样被存储和表示?图片和其他所有资源一样,在内存中本质上都是0和1的二进制数据,计算机需
2022-05-23

Go语言如何实现图形可视化功能

Go语言如何实现图形可视化功能随着计算机技术的不断进步,图形可视化已经成为了信息传递和呈现的重要方式之一。在开发应用程序时,如何利用编程语言实现图形可视化功能成为了一个重要课题。Go语言是一门越来越受欢迎的编程语言,它凭借其简洁、高效的特
Go语言如何实现图形可视化功能
2024-03-11

c++如何实现图形化界面

C++本身是一种面向对象的编程语言,不直接提供图形化界面的功能。但是可以通过使用第三方库或者框架来实现图形化界面。以下是几种常见的C++图形化界面开发工具:1. Qt:Qt 是一个跨平台的C++应用程序开发框架,它提供了一套丰富的图形化界面
2023-09-14

css如何实现图形化边界

这篇文章给大家分享的是有关css如何实现图形化边界的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。图形化边界  顾名思义,图形化边界就是允许使用图片作为对象的边界,语法如下:  border: 5px solid
2023-06-27

PHP 函数的性能优化如何实现?

优化 php 函数性能的关键技术:缓存结果避免重复运算减少函数调用复杂度,分解为简单函数使用 php 内置优化函数避免不必要的类型转换实战案例:缓存 get_product_data 函数从数据库获取的产品数据,提升了函数性能。PHP 函数
PHP 函数的性能优化如何实现?
2024-04-18

C++图形编程:优化性能的深入探讨

提升c++++图形编程性能的深入探索:硬件优化:选择高性能显卡和多核处理器。代码优化:避免不必要拷贝,优化数据结构,使用内联和汇编。渲染优化:分批渲染,使用层次细分,避免过度绘制。实战案例:并行加载场景对象,优化复杂模型的渲染。C++图形编
C++图形编程:优化性能的深入探讨
2024-05-10

win101909性能如何优化

这篇文章主要介绍了win101909性能如何优化的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇win101909性能如何优化文章都会有所收获,下面我们一起来看看吧。一、加速开机速度 1.【Win】+【R】打开【
2023-07-01

Laravel如何优化性能

这篇文章主要讲解了“Laravel如何优化性能”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Laravel如何优化性能”吧!Laravel 的性能优化为何如此重要?这篇文章将介绍几个重要的技
2023-07-04

如何优化Hibernate性能

这篇文章主要介绍“如何优化Hibernate性能”,在日常操作中,相信很多人在如何优化Hibernate性能问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”如何优化Hibernate性能”的疑惑有所帮助!接下来
2023-06-17

Apache性能如何优化

Apache性能优化的方法有很多种,以下是一些常用的方法:使用高效的操作系统和硬件:使用最新的操作系统和硬件可以提高服务器的性能。调整Apache配置:对Apache的配置进行调整可以提高性能。比如调整连接超时时间、调整最大并发连接数等。启
Apache性能如何优化
2024-07-04

怎么实现PHP性能优化

这期内容当中小编将会给大家带来有关怎么实现PHP性能优化,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。PHP语言功能虽然强大,但是也需要程序员们编写规范,创造出性能优越的代码程序。下面我们就为大家一起分享
2023-06-17

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录