为什么drawRect的空实现会在动画期间对性能产生负面影响 [英] Why an empty implementation of drawRect: will adversely affect performance during animation

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

问题描述

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

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

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



1)为什么<$ c的空实现$ c> 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应用程序以不同的方式告诉您。


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.

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

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