性能优化

UI卡顿优化 :

1. 基于CPU和GPU两方面来回答,尽可能减少CPU、GPU资源消耗

2. 按照60FPS的刷帧率,每隔16ms就会有一次VSync信号

3. 对象的创建会分配内存、设置属性等,会消耗CPU资源。所以尽量使用轻量对象代替,比如能用CALayer的时候尽量不用UIView,敏感位置能不用IB尽量使用纯代码手写。

4. 不要频繁地调用UIView的相关属性,比如frame、bounds、transform等属性,尽量减少不必要的修改

5. 尽量提前计算好布局,在有需要时一次性调整对应的属性,不要多次修改属性

6. Autolayout会比直接设置frame消耗更多的CPU资源

7. 图片的size最好刚好跟UIImageView的size保持一致

8. 控制一下线程的最大并发数量

9. 尽量把耗时的操作放到子线程

预渲染:10 11

10. 文本处理(尺寸计算、绘制) 

11.图片处理(解码、绘制)

12. 尽量避免短时间内大量图片的显示,尽可能将多张图片合成一张进行显示

13. GPU能处理的最大纹理尺寸是4096x4096,一旦超过这个尺寸,就会占用CPU资源进行处理,所以纹理尽量不要超过这个尺寸

14. 尽量减少视图数量和层次

15. 减少透明的视图(alpha<1),不透明的就设置opaque为YES

16. 尽量避免出现离屏渲染和图层混合

17. 使用复用机制,View 的复用和懒加载机制

卡顿掉帧的原因:

在规定的时间之内(16.7ms),在下一帧Vsync信号到来之前,CPU和GPU并没有完成下一帧画面的的合成 于是造成了掉帧 在滑动的时候就会有卡顿的现象 

卡顿掉帧检测:

可以添加Observer到主线程RunLoop中,通过监听RunLoop状态切换的耗时,以达到监控卡顿的目的

内存优化:

1.  Cell的重用机制,包括UITableView、UICollectionView

2. 循环引用 

循环引用是iOS开发中经常遇到的问题,尤其对于新手来说是个头疼的问题。循环引用对App有潜在的危害,会使内存消耗过高,性能变差和Crash等

3. Autoreleasepool的正确使用

4. 合理使用cache

如何计算图片加载内存中所占的大小:

图片内存大小的计算公式 宽度 * 高度 * bytesPerPixel/8。

bytesPerPixel : 每个像素所占的字节数。

RGBA颜色空间下 每个颜色分量由32位组成

所以一般图片的计算公式是 宽度 x 高度 x 4

检测工具:

1、Xcode Memory Debugger

2、Instruments Analyse Alloctions

3、MLeaksFinder

泄露的内存主要有以下两种:

Laek Memory 这种是忘记 Release 操作所泄露的内存。

Abandon Memory 这种是循环引用,无法释放掉的内存。

耗电优化:

1. 尽可能降低CPU、GPU功耗

2. 少用定时器

3. 优化I/O操作,尽量不要频繁写入小数据,最好批量一次性写入

4. 数据量比较大的,建议使用数据库(比如SQLite、CoreData)

5. 读写大量重要数据时,考虑用dispatch_io,其提供了基于GCD的异步操作文件I/O的API。用dispatch_io系统会优化磁盘访问

检测:

Instrument Energy Diagnostics

网络优化:

1. 减少、压缩网络数据

2. 如果多次请求的结果是相同的,尽量使用缓存

3. 使用断点续传,否则网络不稳定时可能多次传输相同的内容

4. 网络不可用时,不要尝试执行网络请求

5. 让用户可以取消长时间运行或者速度很慢的网络操作,设置合适的超时时间

定位优化:

1. 如果只是需要快速确定用户位置,最好用CLLocationManager的requestLocation方法。定位完成后,会自动让定位硬件断电

2. 如果不是导航应用,尽量不要实时更新位置,定位完毕就关掉定位服务

3. 尽量降低定位精度,比如尽量不要使用精度最高的kCLLocationAccuracyBest

4. 需要后台定位时,尽量设置pausesLocationUpdatesAutomatically为YES,如果用户不太可能移动的时候系统会自动暂停位置更新

5. 尽量不要使用startMonitoringSignificantLocationChanges,优先考虑startMonitoringForRegion:

APP的启动:

启动时间 = pre-main耗时+main耗时

应该在400ms内完成main()函数之前的加载

整体过程耗时不能超过20秒,否则系统会kill掉进程,App启动失败

启动前:

1. 尽量使用静态库,减少动态库的使用,动态链接比较耗时,如果要用动态库,尽量将多个dylib动态库合并成一个

2. 尽量避免对系统库使用optional linking,如果App用到的系统库在你所有支持的系统版本上都有,就设置为required,因为optional会有些额外的检查

3. 减少Objc类、分类的数量、减少Selector数量(定期清理不必要的类、分类)

4. 减少C++虚函数数量

5. 将不必须在+load中做的事情尽量挪到+initialize中,+initialize是在第一次初始化这个类之前被调用,+load在加载类的时候就被调用。尽量将+load里的代码延后调用

6. 删减一些无用的静态变量,删减没有被调用到或者已经废弃的方法

7. 对application:didFinishLaunchingWithOptions:里的任务尽量延迟加载或懒加载

8. 不要在NSUserDefaults中存放太多的数据,NSUserDefaults是一个plist文件,plist文件会被反序列化一次

9. 二进制文件重排优化启动速度  基于Clang SanitizerCoverage 的工具 AppOrderFiles。CocoaPods 接入,一行调用生成 Order File。 GitHub地址:https://github.com/yulingtianxia/AppOrderFiles

安装包瘦身:

1. 编写LLVM插件检测出重复代码、未被调用的代码

2. 去除没有用到的资源: https://github.com/tinymind/LSUnusedResources

3. 资源(图片、音频、视频等)采取无损压缩

使用Image Asset Catalogs,  Apple推荐的图片资源管理工具,压缩效率更高,在iOS 12的机器上有10~20%的空间节约,并且每个版本Apple都会持续对其进行优化。

分为:

压缩图片质量

一般情况下使用UIImageJPEGRepresentation或UIImagePNGRepresentation方法实现。

压缩图片尺寸

一般通过指定压缩的大小对图像进行重绘

4. 利用AppCode(https://www.jetbrains.com/objc/)检测未使用的代码:菜单栏 -> Code -> Inspect Code

代码优化:

(1) 头文件导入的时候 最好使用向后导入

原因:

不在A的头文件中引入B的头文件,就不会一并引入B的全部内容,这样就减少了编译时间。

可以避免循环引用:因为如果两个类在自己的头文件中都引入了对方的头文件,那么就会导致其中一个类无法被正确编译

(2) 去掉NSlog打印

(3) 通过LLVM检测重复代码未调用代码

(4) 多用枚举表示状态 选项 状态码

(5) 多用类型常量 少用#define 预处理命令

(6) @implementation 不要写在头文件,尤其是这个头文件可能被 import 到多个地方的情况下!!!

否则会影响 ①包体积 ②消息发送时间(尤其是高频使用的类)

1. UITableView怎么优化?(缓存高度,异步绘制,减少层级,hide,避免离屏渲染)

缓存高度:当我们创建frame模型的时候,计算出来cell的高度的时候,我们可以将cell的高度缓存到字典里面,以cell的indexpath和Identifier作为为key。

NSString *key = [[HeightCache shareHeightCache] makeKeyWithIdentifier:@"YwywProductGradeCell" indexPath:indexPath];

if ([[HeightCache shareHeightCache] existInCacheByKey:key]) {

    return [[HeightCache shareHeightCache] heightFromCacheWithKey:key];

}else{

    YwywProductGradeModelFrame *modelFrame = self.gradeArray[indexPath.row];

    [[HeightCache shareHeightCache] cacheHieght:modelFrame.cellHight key:key];

    return modelFrame.cellHight;

}

异步绘制、减少层级:

避免离屏渲染:只要不是同时使用边框/边框颜色以及圆角的时候,都可以使用layer直接设置。不会造成离屏渲染。

2. 如何定位项目中影响性能的地方?以及如何进行性能优化?

3.

4. AppDelegate如何瘦身?

5.App 启动优化策略?最好结合启动流程来说(main()函数的执行前后都分别说一下,知道多少说多少)

一个 App 的启动时间从侧面可以反映出这个 App 的质量。启动时间一般分为两部分:Pre-main Time 和 Loading Time。Loading Time 指的是程序入口 main 函数到 AppDelegate 的applicationDidBecomeActive方法之间的耗时

6.容错处理你们一般是注意哪些?

在团队协作开发当中,由于每个团队成员的水平不一,很难控制代码的质量,保证代码的健壮性,经常会发生由于后台返回异常数据造成app崩溃闪退的情况,为了避免这样的情况项目中做一些容错处理,显得格外重要,极大程度上降低了因为数据容错不到位产生崩溃闪退的概率。

例如:

1.字典

2.数组;

3.野指针;

4.NSNull

等~

7.如果项目开始容错处理没做?如何防止拦截潜在的崩溃?

例:

1、category给类添加方法用来替换掉原本存在潜在崩溃的方法。

2、利用runtime方法交换技术,将系统方法替换成类添加的新方法。

3、利用异常的捕获来防止程序的崩溃,并且进行相应的处理。

总结:

1、不要过分相信服务器返回的数据会永远的正确。

2、在对数据处理上,要进行容错处理,进行相应判断之后再处理数据,这是一个良好的编程习惯。

8.对于性能优化的方案?

9. App 网络层有哪些优化策略?

10. 

11.程序crash的原因有哪些?


12.如何捕获异常?

13.如何检测项目中的卡顿问题(比如假死)?

Core Animation,Instruments里的图形性能问题的测试工具。

view debugging,Xcode 自带的,视图层级。

reveal,视图层级。.

14.讲如何将一张内存极大的图片可以像地图一样的加载出来(只说实现思路)

使用CATiledLayer加载大图,tile layer设置一个缩放区域的集合和重绘阈值,让scroll view在缩放时,绘制层根据这些区域和缩放阈值去重新绘制当前显示的区域

15. fps是怎么计算的?除了用cadisplay,还有什么方法吗?

16. leaks怎么实现?如何代码实现监听僵尸对象?

17. AppDelegate如何瘦身?

18.App 启动优化策略?最好结合启动流程来说(main()函数的执行前后都分别说一下,知道多少说多少)?

19. 你知道有哪些情况会导致app卡顿,分别可以用什么方法来避免?(知道多少说多少)

20.App 网络层有哪些优化策略?

21.什么是离屏渲染?什么情况下会触发?怎么检测?该如何应对?

GPU屏幕渲染有两种方式:

(1)On-Screen Rendering (当前屏幕渲染) 

指的是GPU的渲染操作是在当前用于显示的屏幕缓冲区进行。

(2)Off-Screen Rendering (离屏渲染)

指的是在GPU在当前屏幕缓冲区以外开辟一个缓冲区进行渲染操作。

当前屏幕渲染不需要额外创建新的缓存,也不需要开启新的上下文,相对于离屏渲染性能更好。但是受当前屏幕渲染的局限因素限制(只有自身上下文、屏幕缓存有限等),当前屏幕渲染有些情况下的渲染解决不了的,就使用到离屏渲染。

相比于当前屏幕渲染,离屏渲染的代价是很高的,主要体现在两个方面:

(1)创建新缓冲区

要想进行离屏渲染,首先要创建一个新的缓冲区。

(2)上下文切换

离屏渲染的整个过程,需要多次切换上下文环境:先是从当前屏幕(On-Screen)切换到离屏(Off-Screen),等到离屏渲染结束以后,将离屏缓冲区的渲染结果显示到屏幕上有需要将上下文环境从离屏切换到当前屏幕。而上下文环境的切换是要付出很大代价的。

下面的情况或操作会引发离屏渲染:

- 为图层设置遮罩(layer.mask)

- 设置圆角时,将图层的layer.masksToBounds / view.clipsToBounds属性设置为true 

- 将图层layer.allowsGroupOpacity属性设置为YES和layer.opacity小于1.0

- 为图层设置阴影(layer.shadow *)。

- 为图层设置layer.shouldRasterize=true

- 具有layer.cornerRadius,layer.edgeAntialiasingMask,layer.allowsEdgeAntialiasing的图层

- 文本(任何种类,包括UILabel,CATextLayer,Core Text等)。

- 使用CGContext在drawRect :方法中绘制大部分情况下会导致离屏渲染,甚至仅仅是一个空的实现。

优化方案

官方对离屏渲染产生性能问题也进行了优化:

iOS 9.0 之前UIimageView跟UIButton设置圆角都会触发离屏渲染。

iOS 9.0 之后UIButton设置圆角会触发离屏渲染,而UIImageView里png图片设置圆角不会触发离屏渲染了,如果设置其他阴影效果之类的还是会触发离屏渲染的。

1、圆角优化

优化方案1:使用贝塞尔曲线UIBezierPath和Core Graphics框架画出一个圆角

UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(100,100,100,100)];

imageView.image = [UIImage imageNamed:@"myImg"];

//开始对imageView进行画图

UIGraphicsBeginImageContextWithOptions(imageView.bounds.size,NO,1.0);

//使用贝塞尔曲线画出一个圆形图

[[UIBezierPath bezierPathWithRoundedRect:imageView.boundscornerRadius:imageView.frame.size.width]addClip];

[imageView drawRect:imageView.bounds];

imageView.image=UIGraphicsGetImageFromCurrentImageContext();

//结束画图

UIGraphicsEndImageContext();

[self.view addSubview:imageView];

优化方案2:使用CAShapeLayer和UIBezierPath设置圆角

UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(100, 100, 100, 100)];

imageView.image = [UIImage imageNamed:@"myImg"];

UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:imageView.bounds byRoundingCorners:UIRectCornerAllCorners cornerRadii:imageView.bounds.size];

CAShapeLayer *maskLayer = [[CAShapeLayer alloc]init];

//设置大小

maskLayer.frame = imageView.bounds;

//设置图形样子

maskLayer.path = maskPath.CGPath;

imageView.layer.mask = maskLayer;

[self.view addSubview:imageView];

对于方案2需要解释的是:

CAShapeLayer继承于CALayer,可以使用CALayer的所有属性值;

CAShapeLayer需要贝塞尔曲线配合使用才有意义(也就是说才有效果)

使用CAShapeLayer(属于CoreAnimation)与贝塞尔曲线可以实现不在view的drawRect(继承于CoreGraphics走的是CPU,消耗的性能较大)方法中画出一些想要的图形

CAShapeLayer动画渲染直接提交到手机的GPU当中,相较于view的drawRect方法使用CPU渲染而言,其效率极高,能大大优化内存使用情况。

总的来说就是用CAShapeLayer的内存消耗少,渲染速度快,建议使用优化方案2。

3、其他的一些优化建议

当我们需要圆角效果时,可以使用一张中间透明图片蒙上去

使用ShadowPath指定layer阴影效果路径

使用异步进行layer渲染(Facebook开源的异步绘制框架AsyncDisplayKit)

设置layer的opaque值为YES,减少复杂图层合成

尽量使用不包含透明(alpha)通道的图片资源

尽量设置layer的大小值为整形值

直接让美工把图片切成圆角进行显示,这是效率最高的一种方案

很多情况下用户上传图片进行显示,可以让服务端处理圆角

使用代码手动生成圆角Image设置到要显示的View上,利用UIBezierPath(CoreGraphics框架)画出来圆角图片

Core Animation工具检测离屏渲染

对于离屏渲染的检测,苹果为我们提供了一个测试工具Core Animation。可以在Xcode->Open Develeper Tools->Instruments中找到

下面我们来说明每个选项的功能:

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

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

Color Copied Images:按照官方的说法,当图片的颜色格式GPU不支持的时候,Core Animation会

Color Immediately:默认情况下Core Animation工具以每毫秒10次的频率更新图层调试颜色,如果勾选这个选项则移除10ms的延迟。对某些情况需要这样,但是有可能影响正常帧数的测试。

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:当对图层重绘的时候回显示黄色,如果频繁发生则会影响性能。可以用增加缓存来增强性能。

CPU处理,Processing

网络,Networking

定位,Location

图像,Graphics

22. 怎么检测图层混合?

怎么检测图层混合

1、模拟器debug中color blended layers红色区域表示图层发生了混合

2、Instrument-选中Core Animation-勾选Color Blended Layers

避免图层混合:

确保控件的opaque属性设置为true,确保backgroundColor和父视图颜色一致且不透明

如无特殊需要,不要设置低于1的alpha值

确保UIImage没有alpha通道

UILabel图层混合解决方法:

iOS8以后设置背景色为非透明色并且设置label.layer.masksToBounds=YES让label只会渲染她的实际size区域,就能解决UILabel的图层混合问题

iOS8 之前只要设置背景色为非透明的就行

为什么设置了背景色但是在iOS8上仍然出现了图层混合呢?

UILabel在iOS8前后的变化,在iOS8以前,UILabel使用的是CALayer作为底图层,而在iOS8开始,UILabel的底图层变成了_UILabelLayer,绘制文本也有所改变。在背景色的四周多了一圈透明的边,而这一圈透明的边明显超出了图层的矩形区域,设置图层的masksToBounds为YES时,图层将会沿着Bounds进行裁剪 图层混合问题解决了

23. 

你可能感兴趣的:(性能优化)