前不久我作了一个富文本编辑工具,编辑器遇到了一个性能问题是添加多张图片,当滚动编辑区域,遇到图片切换的时候会有明显的卡顿现象。这篇文章基于这个卡顿的性能问题进行性能瓶颈的分析以及作对应的优化。bash
能够打开这个连接 iOS使用UITableView实现的富文本编辑器 查看个人文章,这篇文章所用的项目也是基于这个项目的。网络
最终的分析优化的结果把时间从90ms的数量级下降到了2ms的数量级,达到了一个比较流畅的效果。具体的分析优化步骤请往下看。编辑器
既然问题是发生在图片切换的时候,图片是放在单独的一个Cell中的,那么就尝试在Cell的渲染方法 cellForRowAtIndexPath
添加两个Log,查看方法执行所用的时间。
函数
2017-08-11 06:12:48.744 RichTextEditDemo[6867:1064632] ======begin render cell
2017-08-11 06:12:48.749 RichTextEditDemo[6867:1064632] ======end render cell
2017-08-11 06:12:49.261 RichTextEditDemo[6867:1064632] ======begin render cell
2017-08-11 06:12:49.266 RichTextEditDemo[6867:1064632] ======end render cell
复制代码
从日志打印的时间上看,大概每渲染一个Cell只要发几毫秒的时间,貌似问题不会出如今这个位置,然而这并非真相,很明显的,其余地方不会影响到,因此得用更高级的分析工具去分析查看。工具
Instrument是一个很好的性能分析工具,能够分析内存分配、内存泄漏、网络状况、CPU占用等和性能有关的问题,当前的性能问题是耗时的问题,可使用 Instrument 的 Time Profiler 进行分析
让这个列表滚动,而且有进行图片Cell的切换性能
能够看到Time Profiler 有下面的记录,红色框中就是Cell切换所耗费的时间值,这个时间的增加很明显的高于其余值了,因此这个就是咱们要定位到的地方了。优化
Tipsui
第一步要在时间轴上框选一个范围,标识选择这个范围进行分析,才能准肯定位到这个问题,如图(1)位置所示;第二步要选在堆栈中的某一个函数,通常的选择到OC函数调用,更底层的函数调用就到了CF层是C语言实现的就很差分析了,因此这里选择的是 [UIImage drawInRect:blendMode:alpha]
这个函数分析,能够看到这个函数调用说花费的时间是 92ms,这是一个比较长的时间了,因此应该就是这里致使的卡顿了。spa
这个函数花费的时间和image图片的大小有关系的,选择另外一个时间峰值范围,这个时间峰值范围是发生在小图之间的切换的.net
这个地方耗费的时间就比较小一点,不过也是达到了25ms,对于性能也是有必定的影响的。
以上的分析能够得出结论:[UIImage drawInRect:blendMode:alpha]
函数的调用是会致使性能问题的,由于UITextView内部处理图片的方式是经过调用 [UIImage drawInRect:blendMode:alpha]
函数绘制图片实现的。
既然是UITextView内部的处理方式,因此这个函数调用行为是应用层改变不了的,不过UIImage对象是咱们能够控制的,或者能够改变图片的显示方式来达到优化的目的,因此就有了如下的两种方案。
第一种方案就是对预览的图片进行压缩,而后再设置到NSTextAttachmen中,放到UITextView中显示
textAttachment.image = self.image;
// ===> 修改成
// scaletoSize用于压缩原始的图片,textAttachment中的image对象是压缩事后的
textAttachment.image = [self.image scaletoSize:showImageWidth];
复制代码
这样修改以后大图的滚到加载时间减小到了40ms左右
上面的方案进行了图片的压缩,时间的耗费仍是由于 [UIImage drawInRect:blendMode:alpha]
函数的调用,因此有没有一种更好的方案呢?答案是确定的,能够把传给UITextView的image压缩成一个很小的,(这一步也能够没必要,传递一个空的UIImageView对象便可,这里设置图片的主要缘由是图片区域须要一个编辑的光标),而后在 UITextView 所对应的图片区域添加一个UIImageView,在UIImageView中设置原始的图片便可,这种方案会比方案1的效果好不少。
方案二几个修改点:
//....
NSTextAttachment *textAttachment = [[NSTextAttachment alloc] init];
CGRect rect = CGRectZero;
rect.size.width = showImageWidth;
rect.size.height = showImageHeight;
textAttachment.bounds = rect;
textAttachment.image = [UIImage new];
NSAttributedString *attachmentString = [NSAttributedString attributedStringWithAttachment:textAttachment];
//....
复制代码
[self.imageContentView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self).offset(_imageModel.imageFrame.origin.x);
make.top.equalTo(self).offset(_imageModel.imageFrame.origin.y);
make.height.equalTo(@(_imageModel.imageFrame.size.height));
make.width.equalTo(@(_imageModel.imageFrame.size.width));
}];
复制代码
下面是使用方案2优化以后的分析图
cellForRowAtIndexPath
方法总共占用了2ms的时间,从分析的堆栈中能够看到
UITextView setAttributedText:
方法才占用了1ms的时间,因此这个提高是很明显的,由于传递了一个空的UIImageView对象,不用执行
[UIImage drawInRect:blendMode:alpha]
方法,使用了UIImageView直接设置Image的方式几乎不会占用时间,因此堆栈中看不到
[UIImageView setImage:]
方法调用的时间。
Instrument是一个很好工具,你用它能够很方便的帮咱们定位到性能问题,问题找到了,那么也就很容易找到解决方案了。