内容简介:在屏幕成像的过程中,负责:对象的创建和销毁、对象属性的调整、布局计算、文本的计算和排版、图片的格式转换和解码、图像的绘制(负责:纹理的渲染。
tableView
CPU
和 GPU
在屏幕成像的过程中, CPU
和 GPU
起着至关重要的作用。
-
CPU
(Central Processing Unit
,中央处理器)
负责:对象的创建和销毁、对象属性的调整、布局计算、文本的计算和排版、图片的格式转换和解码、图像的绘制( Core Graphics
)。
-
GPU
(Graphics Processing Unit
,图形处理器)
负责:纹理的渲染。
graph LR; CPU-->|计算|GPU; GPU-->|渲染|帧缓存; 帧缓存-->|读取|视频控制器; 视频控制器-->|显示|屏幕;
CPU
计算好的数据给 GPU
, GPU
来渲染,渲染后的数据放在帧缓存(缓冲区,有两块缓冲区,前帧缓存和后帧缓存,协调使用,效率高)中。然后, 视频控制器
从缓冲区获取渲染后的数据显示在 屏幕
上。
-
在
iOS
中是双缓冲机制, 有前帧缓存、后帧缓存。
屏幕成像原理
一帧(或者一页)数据就是:一个垂直同步信号( VSync
)和一个水平同步信号( HSync
)的组合。
先发送一个垂直同步信号( VSync
),代表即将显示一页,再发送一个水平同步信号( HSync
)就显示一帧。
卡顿产生的原因
graph LR; CPU计算-->GPU渲染计算; GPU渲染计算-->VSync信号 VSync信号-.-....;
当下次 VSync
信号到来之前, CPU
和 GPU
还没有计算完成,就会产生卡顿效果。
- 卡顿解决的主要思路
尽可能减少 CPU
、 GPU
资源消耗。
-
按照
60FPS
的刷帧率,每隔16ms
就会有一次VSync
信号。
卡顿优化 - CPU
-
尽量用轻量级的对象,比如用不到事件处理的地方,可以考虑使用
CALayer
取代UIView
。 -
不要频繁地调用
UIView
的相关属性, 比如frame
、bounds
、transform
等属性,尽量减少不必要的修改。 -
尽量提前计算好布局,在有需要时一次性调整对应的属性,不要多次修改属性。
-
Autolayout
会比直接设置frame
消耗更多的CPU
资源。 -
图片的
size
最好刚好跟UIImageView
的size
保持一致。 -
控制一下线程的最大并发数量。
-
尽量把耗时的操作放到子线程。
文本处理(尺寸计算、绘制)
图片处理(解码、绘制)
// 文字计算 [@"text" boundingRectWithSize:CGSizeMake(100, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:nil context:nil]; // 文字绘制 [@"text" drawWithRect:CGRectMake(0, 0, 100, 100) options:NSStringDrawingUsesLineFragmentOrigin attributes:nil context:nil];
graph LR; UIImage+imageNamed:-->|压缩|二进制文件; 二进制文件-->|解码|屏幕所需要的格式; 屏幕所需要的格式-->渲染到屏幕;
图片 解码
的过程默认是在主线程,如果图片比较多数据比较大,就会产生卡顿。提前在子线程 解码
。
// 图片的处理,提前解码 - (void)image { UIImageView *imageView = [[UIImageView alloc] init]; imageView.frame = CGRectMake(100, 100, 100, 56); [self.view addSubview:imageView]; self.imageView = imageView; dispatch_async(dispatch_get_global_queue(0, 0), ^{ // 获取CGImage CGImageRef cgImage = [UIImage imageNamed:@"timg"].CGImage; // alphaInfo CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(cgImage) & kCGBitmapAlphaInfoMask; BOOL hasAlpha = NO; if (alphaInfo == kCGImageAlphaPremultipliedLast || alphaInfo == kCGImageAlphaPremultipliedFirst || alphaInfo == kCGImageAlphaLast || alphaInfo == kCGImageAlphaFirst) { hasAlpha = YES; } // bitmapInfo CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host; bitmapInfo |= hasAlpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst; // size size_t width = CGImageGetWidth(cgImage); size_t height = CGImageGetHeight(cgImage); // 解码:把位图提前画到图形上下文,生成 cgImage,就完成了解码。 // context CGContextRef context = CGBitmapContextCreate(NULL, width, height, 8, 0, CGColorSpaceCreateDeviceRGB(), bitmapInfo); // draw CGContextDrawImage(context, CGRectMake(0, 0, width, height), cgImage); // get CGImage cgImage = CGBitmapContextCreateImage(context); // 解码后的图片,包装成 UIImage 。 // into UIImage UIImage *newImage = [UIImage imageWithCGImage:cgImage]; // release CGContextRelease(context); CGImageRelease(cgImage); // back to the main thread dispatch_async(dispatch_get_main_queue(), ^{ self.imageView.image = newImage; }); }); }
卡顿优化 - GPU
-
尽量避免短时间内大量图片的显示,尽可能将多张图片合成一张进行显示。
-
GPU
能处理的最大纹理尺寸是4096x4096
,一旦超过这个尺寸,就会占用CPU
资源进行处理,所以纹理尽量不要超过这个尺寸。 -
尽量减少视图数量和层次。
-
减少透明的视图(
alpha<1
),不透明的就设置opaque
为YES
。(透明视图叠加,需要计算叠加部分颜色。) -
尽量避免出现离屏渲染。
离屏渲染
-
在
OpenGL
中,GPU
有2
中渲染方式:On-Screen Rendering
:当前屏幕渲染,在当前用于显示的屏幕缓冲区进行渲染操作。Off-Screen Rendering
:离屏渲染,在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作。
- 离屏渲染消耗性能的原因:
需要创建新的缓冲区。
离屏渲染的整个过程,需要多次切换上下文环境。先是从当前屏幕( On-Screen
)切换到离屏( Off-Screen
);等到离屏渲染结束以后,将离屏缓冲区的渲染结果显示到屏幕上,又需要将上下文环境从离屏切换到当前屏幕。
-
哪些操作会触发离屏渲染?
光栅化,
layer.shouldRasterize = YES
遮罩,
layer.mask
圆角,同时设置
layer.masksToBounds = YES
、layer.cornerRadius
大于0。可以考虑通过
CoreGraphics
绘制裁剪圆角,或者让美工提供圆角图片。
-
阴影,
layer.shadowXXX
如果设置了 layer.shadowPath
就不会产生离屏渲染。
卡顿检测
- 平时所说的“卡顿”主要是因为在线程执行了比较耗时的操作。
-
可以添加
Observer
到主线程RunLoop
中,通过监听RunLoop
状态切换的耗时,以达到监控卡顿的目的。
耗电
耗电的主要来源
-
CPU
处理,Processing
-
网络,
Networking
-
定位,
Location
-
图像,
Graphics
耗电优化
-
尽可能降低
CPU
、GPU
功耗 -
少用定时器
-
优化
I/O
操作尽量不要频繁写入小数据,最好批量一次性写入。
读写大量重要数据时,考虑用
dispatch_io
,其提供了基于GCD
的异步操作文件I/O
的API
。用dispatch_io
系统会优化磁盘访问。数据量比较大的,建议使用数据库(比如
SQLite
、CoreData
) -
网络优化
减少、压缩网络数据。 XML
:体积比较大。 JSON
:体积比较小。 protobuf
。
graph LR; XML-->JSON; JSON-->protobuf(protocol buffer);
如果多次请求的结果是相同的,尽量使用缓存。(一个请求内容不变的情况, NSMutableURLRequest
中可以使用 NSCache
缓存。)
使用断点续传,否则网络不稳定时可能多次传输相同的内容
网络不可用时,不要尝试执行网络请求
让用户可以取消长时间运行或者速度很慢的网络操作,设置合适的超时时间
批量传输,比如,下载视频流时,不要传输很小的数据包,直接下载整个文件或者一大块一大块地下载。如果下载广告,一次性多下载一些,然后再慢慢展示。如果下载电子邮件,一次下载多封,不要一封一封地下载。
- 定位优化
如果只是需要快速确定用户位置,最好用 CLLocationManager
的 requestLocation
方法。定位完成后,会自动让定位硬件断电。
如果不是导航应用,尽量不要实时更新位置,定位完毕就关掉定位服务。
尽量降低定位精度,比如尽量不要使用精度最高的 KCLLocationAccuracyBest
。
需要后台定位时,尽量设置 pausesLocationUpdatesAutomatically
为 YES
, 如果用户不太可能移动的时候系统会自动暂停位置更新。
尽量不要使用 startMonitoringSignificantLocationChanges
优先考虑 startMonitoringForRegion:
。
- 硬件检测优化
用户移动、摇晃、倾斜设备时,会产生动作( motion
)事件,这些事件由加速计、陀螺仪、磁力计等硬件检测。在不需要检测的场合,应该及时关闭这些硬件。
App 的启动
-
App 的启动可以分为
2
种:
冷启动( Cold Launch
):从零开始启动 App
热启动( Warm Launch
): App
已经在内存中,在后台存活着,再次点击图标启动 App
。
-
App
启动时间的优化,主要是针对冷启动进行优化。 -
通过添加环境变量可以打印出
App
的启动时间分析(Edit scheme -> Run -> Arguments
)DYLD_PRINT_STATISTICS
设置为1
。如果需要更详细的信息,那就将DYLD_PRINT_STATISTICS_DETAILS
设置为1
。(总启动时间差不多400ms内就是正常)
-
App
的冷启动可以概括为3大阶段:
dyld
runtime
main
App 的启动 dyld
-
dyld
(dynamic link editor
),Apple
的动态连接器,可以用来装载Mach-O
文件(可执行文件、动态库等) -
启动
App
时,dyld
所做的事情有
装载 App
的可执行文件,同时会递归加载所有依赖的动态库。
当 dyld
把可执行文件、动态库都装载完毕后,会通知 Runtime
进行下一步的处理。
App 的启动 - runtime
-
启动
App
时,runtime
所做的事情有:-
调用
map_images
进行可执行文件内容的解析和处理
-
在
load_images
中调用call_load_methods
,调用所有Class
和Category
的+load
方法
-
进行各种
objc
结构的初始化(注册Objc
类、初始化类对象等等)
-
调用
C++
静态初始化器和__attribute__((constructor))
修饰的函数
-
调用
-
到此为止,可执行文件和动态库中所有的符号(
Class
,Protocol
,Selector
,IMP
,…)都已经按格式成功加载到内存中,被runtime
所管理。
App 的启动 - main
- 总结一下
App
的启动由 dyld
主导,将可执行文件加载到内存,顺便加载所有依赖的动态库。
并由 runtime
负责加载成 objc
定义的结构。
所有初始化工作结束后, dyld
就会调用 main
函数。
接下来就是 UIApplicationMain
函数, AppDelegate
的 application:didFinishLaunchingWithOptions:
方法
App 的启动优化
-
按照不同的阶段
-
dyld
减少动态库、合并一些动态库(定期清理不必要的动态库)
减少 Objc
类、分类的数量、减少 Selector
数量(定期清理不必要的类、分类)
减少 C++
虚函数数量
Swift
尽量使用 struct
- runtime
用 +initialize
方法和 dispatch_once
取代所有的 __attribute__((constructor))
、 C++
静态构造器、 Objective-C
的 +load
。
// 替代 +load + (void)initialize { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ }); }
- main
在不影响用户体验的前提下,尽可能将一些操作延迟,不要全部都放在 finishLaunching
方法中。
按需加载。
安装包瘦身
-
安装包(
ipa
)主要由可执行文件、资源组成。 -
资源(图片、音频、视频等)
采取无损压缩
去除没有用到的资源: https://github.com/tinymind/LSUnusedResources
- 可执行文件瘦身
编译器优化
Strip Linked Product
、 Make Strings Read-Only
、 Symbols Hidden by Default
设置为 YES
。
去掉异常支持, Enable C++ Exceptions
、 Enable Objective-C Exceptions
设置为 NO
, Other C Flags
添加 -fno-exceptions
利用 AppCode
( https://www.jetbrains.com/objc/)检测未使用的代码:`菜单->Code->Inspect
Code`
编写 LLVM
插件检测出重复代码、未被调用的代码。
- LinkMap
生成 LinkMap
文件, 可以查看可执行文件的具体组成。
可借助第三方 工具 解析 LinkMap
文件: https://github.com/huanxsd/LinkMap
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 性能优化第一课:性能指标
- 【前端性能优化】vue性能优化
- Golang 性能测试 (2) 性能分析
- 【前端性能优化】02--vue性能优化
- Java性能 -- 性能调优标准
- Java性能 -- 性能调优策略
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
CSS商业网站布局之道
朱印宏 / 清华大学出版社 / 2007-1 / 75.00元
本书是一本CSS技术专著。 主要从布局角度全面、系统和深入地讲解CSS在标准网站布局之中的应用。很多读者经过初步的学习之后就能够使用CSS设计出一些漂亮的网页样式,于是便乐在其中,踌躇满志,这是好事,但千万不要自我陶醉,因为你还未领略CSS的博大精深。用CSS容易,难的是全部都用CSS。CSS的精髓是布局,而不是样式,布局是需要缜密的逻辑思维和系统设计的,而样式只需要简单地编写代码或复制即可。本书......一起来看看 《CSS商业网站布局之道》 这本书的介绍吧!