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