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

sub*_*ngh 8 cocoa-touch objective-c uiview drawrect ios

我是我的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
}
*/
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

uli*_*ess 9

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

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

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

这是显而易见的,考虑到GPU主要在大块中移动未更改的像素.石英确实像素和股网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应用程序以不同的方式告诉您.