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

时间:2013-09-11 18:08:13

标签: ios objective-c cocoa-touch uiview 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:的空实现会在动画期间导致不良表现。

2)我应该何时实施drawRect:

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

2 个答案:

答案 0 :(得分:9)

要知道何时使用-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应用程序以不同的方式告诉您。

答案 1 :(得分:0)

  

Only override draw() if you perform custom drawing.

这意味着如果我们不执行自定义绘图,我们就不应该编写覆盖函数。 "默认"使用draw()。所有图形(如果有的话)都是在本地处理的#34;所以表现最好。

  

An empty implementation adversely affects performance during animation.

如果我们执行了override draw(),但我们没有在函数中编写任何代码,只留下func为空,那就是(1){{1}的实现并且它也是(2)。这会降低性能。以下代码

override draw()

在动画期间效率不高。据推测,系统会尝试重绘整个画布而不是只重绘受影响的部分吗?