背景前不久做了一个富文本编辑工具,小编遇到了添加多张图片的性能问题。滚动编辑区时,遇到图片切换时,偶尔会有明显的卡顿。本文针对这个卡死的性能问题,分析了性能瓶颈,并做了相应的优化。可以打开这个链接使用UITableView实现的iOS富文本编辑器查看我的文章,本文使用的项目也是基于这个项目。结果,最终的分析和优化结果将时间从90ms量级减少到2ms量级,达到了比较流畅的效果。具体分析和优化步骤请看下文。问题分析由于问题出现在切换图片和将图片放在单独的Cell中时,尝试在Cell的渲染方法cellForRowAtIndexPath中加入两个Log,查看该方法执行的耗时。对应结果:2017-08-1106:12:48.744RichTextEditDemo[6867:1064632]======beginrendercell2017-08-1106:12:48.749RichTextEditDemo[6867:1064632]======endrendercell2017-08-1106:12:49.261RichTextEditDemo[6867:1064632]======beginrendercell2017-08-1106:12:49.266RichTextEditDemo[6867:1064632]======endrendercell从日志打印时间来看,大概每次渲染吧一个Cell传输只需要几毫秒,看来这个位置不会出现问题。然而,事实并非如此。显然其他地方是不会受到影响的,所以必须要用更高级的分析工具来分析检查。FindingProblemsInstrument是一个很好的性能分析工具,可以分析内存分配、内存泄漏、网络状况、CPU使用率和其他与性能相关的问题。当前的性能问题是一个耗时问题,可以使用Instrument的TimeProfiler来分析让这个列表滚动,切换图片Cell可以看到TimeProfiler有如下记录。红色方框是Cell切换所花费的时间。这个时间的增加明显高于其他值,所以这就是我们要定位的地方。tipsalt+鼠标滚轮->缩放时间轴shift+鼠标滚轮->移动时间轴按住鼠标框选->选择并定位时间轴第一步是在时间轴上框选一个范围,勾选这个rangetoproceed分析可以准确定位到这个问题,如图(1);第二步选择栈中的某个函数,一般选择OC函数调用,下层函数调用到达CF层,是C语言实现,不好分析,所以[UIImagedrawInRect:这里选择blendMode:alpha]函数分析。可以看到这个函数调用耗费的时间是92ms,是比较长的时间,所以应该是卡顿的原因所在。该函数耗时与图片大小有关。选择另一个时间峰值范围。这次峰值范围是小图像之间发生切换的地方。花费的时间比较少,但也达到了25ms,这对性能也有一定的影响。解决问题上面的分析可以得出一个结论:[UIImagedrawInRect:blendMode:alpha]函数的调用会导致性能问题,因为UITextView内部处理图片的方式是通过调用[UIImagedrawInRect:blendMode:alpha]来绘制图片]的功能。由于是UITextView的内部处理方法,所以这个函数调用的行为是应用层无法改变的,但是UIImage对象是我们可以控制的,或者我们可以改变图片的显示方式来达到优化的目的,于是就有了以下两种方案。解决方案1??**第一种解决方案是将预览图片进行压缩,然后设置为NSTextAttachmen,放在UITextView中显示textAttachment.image=self.image;//===>改为//scaletoSize进行压缩原始图片,textAttachment中的image对象是压缩后的textAttachment.image=[self.imagescaletoSize:showImageWidth];经过这次修改后,大图的roll-to-load时间减少到了40ms左右,虽然时间减少了一半,但是40ms的时间还是比较长,下面会继续优化。方案2上述方案对图片进行了压缩,但是还是因为调用了[UIImagedrawInRect:blendMode:alpha]函数而导致耗时,请问有没有更好的方案呢?答案是可以的,可以通过给UITextView把图片压缩成很小的(这一步也可以不用,只需要传一个空的UIImageView对象,这里设置图片的主要原因是图片区域需要编辑cursor),然后添加一个UIImageView,只需要在UIImageView中设置原始图片即可,这个方案会比方案1效果好很多。方案二:几个修改点:1.将NSTextAttachment的图片设置为一个空的UIImage对象//...NSTextAttachment*textAttachment=[[NSTextAttachmentalloc]init];CGRectrect=CGRectZero;rect.size.width=showImageWidth;rect.size.height=showImageHeight;textAttachment.bounds=rect;textAttachment.image=[UIImagenew];NSAttributedString*attachmentString=[NSAttributedStringattributedStringWithAttachment:textAttachment];//....2.Cell添加ImageView显示Image[self.imageContentViewmas_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对象,不需要执行[UIImagedrawInRect:blendMode:alpha]方法,使用UIImageView直接设置Image几乎不需要时间,所以[UIImagecannotseeninthestack查看setImage:]方法调用时间总结Instrument是一个非常好的工具,可以用它来帮助我们非常方便的定位性能问题,找到问题之后也很容易找到解决方案。
