8

我正在继承我的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:,那么应该采取什么作为最佳实践的预防措施。

4

2 回答 2

10

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

于 2014-04-10T10:24:53.993 回答
0

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
}

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

于 2016-11-28T18:40:21.730 回答