最近工作比较空闲,相信我不说大家都知道性能优化的重要性了吧,很多面试公司都有的要求。这也是作为一个高级软件开发工程师的必备技能 。
zero 卡顿是我们经常遇到的问题,这里就要提到GPU/CPU了
1.为什么会卡顿,在此期间GPU/CPU都做了哪些工作呐?
- CPU:对象的创建/销毁/属性的调整/布局计算/文本的计算和排版/图片格式转换和解码,图像的绘制
- GPU 图像处理器,主要进行纹理的渲染。(纹理是GPU所能识别的一种图像格式)
- IOS是双缓存机制,包括前帧缓存,后帧缓存
- CPU通过一系列的计算,然后GPU去渲染,通过帧缓存之后被视频控制器读取,最后显示到屏幕上。成像的原理是通过水平同步信号+垂直同步信号一帧帧的绘制而成。
- 卡顿的原因:由于CPU要先计算-GPU渲染,假如CPU/GPU处理的事情较多,在固定的帧率下,未完成需要处理的事情,此时垂直同步信号已经到来,两者交叉就会造成视觉上的卡顿
2.卡顿优化
- 尽量减少CPU/GPU的资源消耗
- 不要频繁的调用UIView的相关属性,如frame/bounds/tranform等
- 尽量提前计算好布局,在有需要的时候一次性调整对应的属性
- 尽量吧耗时操作(文本尺寸计算/绘制,图片解码/绘制)放到子线程处理,同时我们在处理线程的时候要注意线程的最大并发数量
one tableView的缓存
1.说到缓存,大家想到的是不是tableviewcell的缓存机制呐
TableViewCell 复用
在cellForRowAtIndexPath:
回调的时候只创建实例,快速返回cell
,不绑定数据。在willDisplayCell: forRowAtIndexPath:
的时候绑定数据(赋值)。
2.TableViewCell
在tableView
滑动时,会不断调用heightForRowAtIndexPath:
,当 cell
高度需要自适应时,每次回调都要计算高度,会导致 UI 卡顿。为了避免重复无意义的计算,需要缓存高度。
怎么缓存
- 字典,NSCache。它可以自动清理系统占用内存且是线程安全的。自动清理时机:往cache内添加新内容时,以及发生内存警告时。
- UITableView-FDTemplateLayoutCell(AutoLayout布局)
- 缓存高度可以避免无意义的计算。自适应高度的计算。推荐sunny的一篇文章请
Two 内存问题
1.常见的问题如下:循环引用,webView的内存泄漏
在IOS 4.2 以后苹果引用了ARC,但是ARC本质与MRC一致,都是通过引用计数管理内存的。但是ARC也不是万能的。也会为了程序的正常运行,会隐式的持有或复制对象。这样变容易造成内存泄漏。其中最常见的就是block的循环引用问题(解决方法这里我就不多解释了)
2.AFnetworking 的内存泄漏问题
使用AF的都知道,需要使用到AFHTTPSessionManager的实例对象。 [AFHTTPSessionManager manager] 我一般都是这样获取的,不知道你们是怎样获取的。但是通过Instrcument工具分析,看到他会出现内存泄漏。解决方法如下:
2.1 给它写个单例
+ (AFHTTPSessionManager *)sharedHTTPSession{
static AFHTTPSessionManager *manager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [AFHTTPSessionManager manager];
return manager;
}
2.2 demo推荐的使用方法
@import AFNetworking;
@interface AFAppDotNetAPIClient : AFHTTPSessionManager
+ (instancetype)sharedClient;
@end
#import "AFAppDotNetAPIClient.h"
static NSString * const AFAppDotNetAPIBaseURLString = @"https://api.app.net/";
@implementation AFAppDotNetAPIClient
+ (instancetype)sharedClient {
static AFAppDotNetAPIClient *_sharedClient = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedClient = [[AFAppDotNetAPIClient alloc] initWithBaseURL:[NSURL URLWithString:AFAppDotNetAPIBaseURLString]];
_sharedClient.securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
});
return _sharedClient;
3.避免主线程堵塞
在主线程中避免复杂的计算堵塞主线程,造成卡顿
4.图片的处理
通常imageNamed :加载mainbundle图片时,函数会缓存加载的image。对于那么使用较少的图片,消耗内存较大 ,所以启动图片设置时,建议使用initWithContentsOfFile:函数。
5. 重用开销大的对象
NSDateFormatter/NSCalendar 特别消耗内存,因为他们的初始化很慢。所以你可以在类中申明一个属性或者懒加载一个他们的实例。
6.选择合适的数据存储
- 使用`NSUerDefaults`-只能用来存储小数据
- 使用XML, JSON, 或者 plist - XML 需要读取整个文件到内存里去解析
- 使用NSCoding存档- 需要读取文件
- 使用类似SQLite的本地SQL数据库
- 使用 Core Data - 苹果建议使用。与SQLite相比性能没啥区别。如果使用SQLite的话建议看下Realm框架。
7.启动时间(这个会在APP启动优化详细介绍)
启动时间很重要,就像人的第一印象,避免启动页中使用庞大的XIB。尽量避免耗时操作,如果需要数据请求的话,尽量在子线程进行
8.学会处理内存警告
UIKit提供了几种收集低内存警告的方法:
- 在app delegate中使用`applicationDidReceiveMemoryWarning:` 的方法
- 在你的自定义UIViewController的子类(subclass)中覆盖`didReceiveMemoryWarning`
- 注册并接收 UIApplicationDidReceiveMemoryWarningNotification 的通知
在这些方法中处理,比如清掉缓存,否则的话会被系统干掉你的应用。
耗电优化
1.耗电的主要来源
- 图像处理
- 定位服务
- 网络请求
- CPU处理
2.耗电优化
- 图片设置圆角的时候尽量让美工提供图片,使用maskToBounds及layer.clipToBounds都会有很大的资源消耗
- 一直在后台定位刷新位置
- 减少定时器的使用
- 减少I/O操作,不要频繁写入小数据,尽量一次读取。读取大数据时可以使用dispatch_io,它会优化磁盘访问。数据量较大时,建议使用数据库。
- 网络优化:减少网络请求的次数,如果数据多次都是相同建议使用缓存。使用断点续传下载数据。让用户取消长时间运行或者网络慢的网络操作,设置合适的网络超时时间。