为什么drawrRect:的空实现会对动画期间的性能产生不利影响 [英] Why an empty implementation of drawrRect: will adversely affects performance during animation

查看:199
本文介绍了为什么drawrRect:的空实现会对动画期间的性能产生不利影响的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  / * 
//只覆盖drawRect :如果执行自定义绘图。
//空白实现会影响动画期间的性能。
- (void)drawRect:(CGRect)rect
{
//绘图代码
}
* /

这在我心中引起了一些问题:



1)为什么一个空的实现 drawRect:会在动画期间造成不良的性能。



2)我应该何时实现 drawRect



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

解决方案

要知道什么时候使用-drawRect:什么时候使用UIImageView,位更多:



UIView和CGLayer主要处理固定图像。这些图像被上传到显卡(如果你知道OpenGL,想象一个图像作为纹理,一个UIView / CGLayer作为一个多边形显示这样的纹理)。一旦图像在GPU上,即使在其他图像之上具有不同水平的α透明度,也可以非常快速地,甚至几次地(以轻微的性能损失)绘制图像。



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子类/ CGLayer)。加载静态内容作为UIImage,并使用UIImageView绘制它,并将在动态生成的内容在运行时在drawRect。如果你有内容反复绘制,但本身不会改变(即在同一个插槽中显示一些状态的3个图标),也使用UIImageView。



一个警告:有一个这样的事情有太多的UIViews。特别透明的区域在GPU上需要更大的费用,因为它们需要在显示时与其后面的其他像素混合。这是为什么你可以将UIView标记为不透明,以指示GPU它可以只是删除该图像后面的一切。



如果你有内容生成动态地在运行时但在应用程序的生命周期的持续时间保持相同(例如,包含用户名的标签),它可能实际上是有意义的,只是绘制整个事情一次使用Quartz,文本,标签的边框等作为一部分的背景。但这通常是不需要的优化,除非Instruments应用程序告诉你不同。


I am subclassing my UIView class. The Xcode (I am using 4.6.3) auto generated code says,

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

It raised few questions in my mind :

1) Why an empty implementation of drawRect: will cause adverse performance during animation.

2) When should I implement drawRect.

3) If I am implementing drawRect: then what should be taken as precaution for the best practice.

解决方案

To know when to use -drawRect: and when to do things like use a UIImageView, I'll have to explain a bit more:

UIView and CGLayer essentially deal in fixed images. These images are uploaded to the graphics card (if you know OpenGL, think of an image as a texture, and a UIView/CGLayer as a polygon showing such a texture). Once an image is on the GPU, it can be drawn very quickly, and even several times, and (with a slight performance penalty) even with varying levels of alpha transparency on top of other images.

CoreGraphics/Quartz is an API for generating images. It takes a pixel buffer (again, think OpenGL texture) and changes individual pixels inside it. This all happens in RAM and on the CPU, and only once Quartz is done, does the image get "flushed" back to the GPU. This round-trip of getting an image from the GPU, changing it, then uploading the whole image (or at least a comparatively large chunk of it) back to the GPU is rather slow. Also, the actual drawing that Quartz does, while really fast for what you are doing, is way slower than what the GPU does.

That's obvious, considering the GPU is mostly moving around unchanged pixels in big chunks. Quartz does random-access of pixels and shares the CPU with networking, audio etc. Also, if you have several elements that you draw using Quartz at the same time, you have to re-draw all of them when one changes, then upload the whole chunk, while if you change one image and then let UIViews or CGLayers paste it onto your other images, you can get away with uploading much smaller amounts of data to the GPU.

When you don't implement -drawRect:, most views can just be optimized away. They don't contain any pixels, so can't draw anything. Other views, like UIImageView, only draw a UIImage (which, again, is essentially a reference to a texture, which has probably already been loaded onto the GPU). So if you draw the same UIImage 5 times using a UIImageView, it is only uploaded to the GPU once, and then drawn to the display in 5 different locations, saving us time and CPU.

When you implement -drawRect:, this causes a new image to be created. You then draw into that on the CPU using Quartz. If you draw a UIImage in your drawRect, it likely downloads the image from the GPU, copies it into the image you're drawing to, and once you're done, uploads this second copy of the image back to the graphics card. So you're using twice the GPU memory on the device.

So the fastest way to draw is usually to keep static content separated from changing content (in separate UIViews/UIView subclasses/CGLayers). Load static content as a UIImage and draw it using a UIImageView and put content generated dynamically at runtime in a drawRect. If you have content that gets drawn repeatedly, but by itself doesn't change (I.e. 3 icons that get shown in the same slot to indicate some status) use UIImageView as well.

One caveat: There is such a thing as having too many UIViews. Particularly transparent areas take a bigger toll on the GPU to draw, because they need to be mixed with other pixels behind them when displayed. This is why you can mark a UIView as "opaque", to indicate to the GPU that it can just obliterate everything behind that image.

If you have content that is generated dynamically at runtime but stays the same for the duration of the application's lifetime (e.g. a label containing the user name) it may actually make sense to just draw the whole thing once using Quartz, with the text, the label's border etc., as part of the background. But that's usually an optimization that's not needed unless the Instruments app tells you differently.

这篇关于为什么drawrRect:的空实现会对动画期间的性能产生不利影响的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆