卡顿优化
屏幕成像原理
视图布局排版、属性(文字大小、颜色)、图片的编码解码等都是CPU去计算的,CPU计算好的数据提交给GPU。 CPU计算的数据是无法直接显示在屏幕上的,屏幕显示的数据有固定的数据格式,数据由GPU渲染后转成屏幕能显示的数据,才能显示。渲染后数据直接放在缓存区,叫着帧缓存。然后将帧缓存里的数据读取到视频控制器这个部件里,然后才能显示。
iOS是双缓冲机制,这个帧缓存满了,还可以放在另一个。或者这一个在"忙",另一个处理。
要显示数据首先要发送一个垂直同步信号,VSync。一旦发送垂直同步信号,就表示要显示一帧(一页)的数据。然后发送一行行的水平同步信号,直到填充整个屏幕为止。
卡顿产生的原因
首先CPU计算的数据交给GPU,然后发送垂直同步信号,然后GPU渲染到帧缓存里的数据显示在屏幕上,完成这一帧的显示,并且马上开始下一帧的操作,开始CPU的计算......
有时候GPU会早早的把数据渲染到帧缓存,然后等待垂直同步信号的到来。
有时候垂直同步信号到来的时候,GPU的渲染还未完成,那么这一帧会显示上一次的数据(上图第三帧会显示第二次的数据),这就造成掉帧。那么第三次的数据是否会丢弃呢,不是,会等待下一个垂直同步信号(上图第四个)的到来,显示在下一帧里。就造成了卡顿。
卡顿优化 CPU
- 文本处理
// 文字计算
[@"text" boundingRectWithSize:CGSizeMake(100, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:**nil** context:**nil**];
// 文字绘制
[@"text" drawWithRect:CGRectMake(0, 0, 100, 100) options:NSStringDrawingUsesLineFragmentOrigin attributes:**nil** context:**nil**];
文本尺寸计算和文本绘制放在子线程。
- 图片处理
UIImageView *imageView = [[UIImageView alloc] init];
imageView.frame = CGRectMake(100, 100, 100, 56);
imageView.image = [UIImage imageNamed:@"test"];
[self.view addSubview:imageView];
self.imageView = imageView;
[UIImage imageNamed:@"test"]得到的是压缩过的图片二进制数据,当图片即将要显示屏幕上,才经过解码操作,解码默认在主线程上操作的,这样如果图片比较多比较大的话,也会产生卡顿。所以要提前放在子线程解码。网络上很多图片第三方库都有异步解码操作。如下面代码:
- (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);
// 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);
// into UIImage CGImage包装成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
- 有透明的视图要经过重新计算。比如两个透明的颜色重叠会变成另一种新的颜色。
离屏渲染
新开辟的缓存区不是在当前屏幕,就是这个缓冲区数据不是用来直接显示在屏幕上的。为什么会产生离屏渲染,是因为当前操作比较耗性能,即使是双缓冲机制也不够用。所以要新开辟缓冲区。
卡顿检测
主线程大部分操作基本在source0和source1,比如点击UI处理,View绘制、计算等等。所有我们要监听结束睡眠到source0这中间所耗的时间。
下面的第三方会监制到造成卡顿的方法,并打印方法调用栈: LXDAppFluecyMonitor
耗电优化
耗电的主要来源
- CPU处理,Processing
- 网络,Networking
- 定位,Location
- 图像,Graphics
耗电优化
- 减少,压缩网络数据:用protobuf(protocol buffer)
- 如果多次请求的结果是相同的,尽量使用缓存 NSMutableURLRequest的NSCache缓存
App启动
- dyld
为什么递归,因为这个动态库有可能依赖另外一个动态库,需要递归一层层查找。
- runtime
map_iamges 在runtime源码
- 总结
- 启动优化
安装包瘦身
去除无用的资源:github.com/tinymind/LS… Appcode: www.jetbrains.com/objc/
- LinkMap
Write Link Map File 是否把可执行文件信息写入LinkMap Path to Link Map File 是LinkMap文件的路径,这里放在桌面。
可借助第三方工具解析。