为什么drawRect的空实现会在动画期间对性能产生负面影响

我是我的UIView类的子类。 Xcode(我使用的是4.6.3)自动生成的代码说,

 /* // Only override drawRect: if you perform custom drawing. // An empty implementation adversely affects performance during animation. - (void)drawRect:(CGRect)rect { // Drawing code } */ 

它在我的脑海里提出了几个问题:

1)为什么drawRect:的空实现drawRect:会在动画期间导致不良性能。

2)什么时候应该实现drawRect: .

3)如果我正在实施drawRect:那么应该采取什么措施作为最佳实践的预防措施。

要知道何时使用-drawRect:以及何时使用UIImageView,我将不得不解释一下:

UIView和CGLayer主要处理固定图像。 这些图像被上传到图形卡(如果您了解OpenGL,则将图像视为纹理,将UIView / CGLayer视为显示此类纹理的多边形)。 一旦图像在GPU上,即使在其他图像之上具有不同的alpha透明度水平,它也可以非常快速地绘制,甚至可以多次绘制(并且具有轻微的性能损失)。

CoreGraphics / Quartz是一个用于生成图像的API。 它需要一个像素缓冲区(再次考虑OpenGL纹理)并更改其中的单个像素。 这一切都发生在RAM和CPU上,只有Quartz完成后,图像才会“刷新”回GPU。 这种从GPU获取图像,更改图像,然后将整个图像(或至少相当大的一部分)上传回GPU的往返行程相当慢。 此外,Quartz所做的实际绘图虽然对你正在做的事情非常快,但却比GPU的速度慢。

这是显而易见的,考虑到GPU主要在大块的未更改像素周围移动。 Quartz对像素进行随机访问,并与网络,音频等共享CPU。此外,如果你有几个元素同时使用Quartz绘制,你必须在一个更改时重新绘制所有元素,然后上传整块,如果你改变一个图像然后让UIViews或CGLayers将它粘贴到你的其他图像上,你就可以将更少量的数据上传到GPU。

当你没有实现-drawRect:时,大多数视图都可以被优化掉。 它们不包含任何像素,因此无法绘制任何内容。 其他视图,如UIImageView,只绘制一个UIImage(同样,它本质上是对纹理的引用,可能已经加载到GPU上)。 因此,如果您使用UIImageView绘制相同的UIImage 5次,它只会上传到GPU一次,然后在5个不同的位置绘制到显示器,从而节省了我们的时间和CPU。

实现-drawRect:时,会导致创建新图像。 然后使用Quartz在CPU上绘制它。 如果您在drawRect中绘制UIImage,它可能会从GPU下载图像,将其复制到您要绘制的图像中,一旦完成,将图像的第二个副本上传回图形卡。 所以你在设备上使用了两倍的GPU内存。

因此,最快的绘制方式通常是将静态内容与更改内容分开(在单独的UIViews / UIView子类/ CGLayers中)。 将静态内容加载为UIImage并使用UIImageView绘制它,并将在运行时动态生成的内容放在drawRect中。 如果您有重复绘制的内容,但本身不会更改(即3个图标显示在同一个插槽中以指示某些状态)也使用UIImageView。

一个警告:有太多UIViews这样的事情。 特别透明的区域在GPU上需要更大的收费来绘制,因为它们在显示时需要与其后面的其他像素混合。 这就是为什么你可以将UIView标记为“不透明”的原因,以向GPU指示它可以消除该图像背后的所有内容。

如果您有在运行时动态生成但在应用程序生命周期内保持不变的内容(例如包含用户名的标签),那么使用Quartz绘制整个内容实际上是有意义的,文本,标签的边框等,作为背景的一部分。 但这通常是一种不需要的优化,除非Instruments应用程序以不同的方式告诉您。

Only override draw() if you perform custom drawing.

这意味着如果我们不执行自定义绘图,我们就不应该编写覆盖函数。 使用“默认” draw() 。 所有图形(如果有)都是“本机”处理的,因此性能最佳。

An empty implementation adversely affects performance during animation.

如果我们override draw() ,但我们不在函数中写入任何代码,只留下此func空,即(1) override draw()实现 ,并且它也是(2)也是空的 。 这会降低性能。 以下代码

 public override func (_ r: CGRect) { // empty } 

在动画期间效率不高。 可能系统会尝试重绘整个canvas而不是仅重绘受影响的部分?