0

我的应用程序中有一个自定义视图(继承自UIView)。自定义视图覆盖

- (void) drawRect:(CGRect) rect

问题是:iPad 3 上的drawRect:执行时间是 iPad 2 上的许多倍(iPad 3 上大约 0.1 秒,iPad 2 上大约 0.003 秒)。它慢了大约 30 倍。

基本上,我使用一些预先创建的图层并将它们绘制在drawRect:. 最后一次通话

CGContextDrawLayerAtPoint(context, CGPointZero, m_currentLayer);

花费大部分时间(大约 95% 的总时间drawRect:

是什么让事情变得如此缓慢,我应该如何解决这个原因?

更新:

没有直接涉及的线程。我确实调用setNeedsDisplay:了一个线程并drawRect:从另一个线程调用,但就是这样。锁也是如此(没有使用锁)。

视图被重绘以响应触摸(它是一个着色书应用程序)。在 iPad 2 上,我在触摸和屏幕更新之间得到了合理的延迟。我想在 iPad 3 上达到同样的效果。

4

2 回答 2

7

所以,iPad 3 在很多方面肯定要慢一些。我对此有一个理论。 Marco Arment 指出,这种方法renderInContext在新 iPad 上速度慢得离谱。在尝试为自定义文本视图创建放大镜时,我也发现了这种情况。最后我不得不放弃renderInContext自定义核心图形绘图。

wait_fences我在这里的核心图形绘图上也遇到了可怕的错误: Only on new iPad 3: wait_fences: failed to receive reply: 10004003

这是我到目前为止所知道的。iPad 3 显然有 4 倍的像素来驱动。这可能会在两个地方引起问题:

  1. 首先,CPU。所有核心图形的绘制都是由 CPU 完成的。在旋转事件的情况下,如果 CPU 绘制时间过长,它会遇到wait_fences错误,我认为这只是一个调用,告诉设备等待更长时间才能实际执行旋转,从而延迟。
  2. 将图像传输到 GPU。GPU 显然可以很好地处理视网膜分辨率(请参阅 Infinity Blade 2)。但是当核心图形绘制时,它会将其图像直接绘制到 GPU 缓冲区以避免 memcpy。然而,要么 GPU 缓冲区自 iPad 2 以来没有变化,要么只是没有使它们变得足够大,因为这些缓冲区很容易过载。发生这种情况时,我相信 CPU 会将图像写入标准内存,然后在 GPU 缓冲区可以处理时将它们复制到 GPU。我认为这是导致性能问题的原因。额外的副本对于如此多的像素非常耗时,并且会大大减慢速度。

为了避免 memcpy,我推荐了几件事:

  1. 只画你需要的。不惜一切代价避免在屏幕外画任何东西。如果您正在绘制一个大视图,但只显示该视图的一部分(例如覆盖它的子视图),请尝试找到一种仅绘制可见内容的方法。
  2. 如果您必须绘制一个大视图,请考虑将视图分解为子视图或子层(在您的情况下可能是子层)。并且只重绘你需要的东西。以知名度应用程序为例。当您放大时,您可以从字面上看到它一次重绘一个正方形。或者在 Safari 中,您可以在滚动时看到它更新方块。不幸的是,我不必这样做,所以我不确定方法。
  3. 尽量保持你的图纸简单。我有一个很棒的自定义核心文本视图,必须在输入的每个字符上重新绘制。非常慢。我将背景更改为简单的白色(在核心图形中),它加速得很好。对我来说最好不要重绘背景。

我想指出,我的理论是猜想。苹果并没有真正解释他们到底做了什么。我的理论只是基于他们所说的以及 iPad 的响应方式以及我自己的实验。

更新

所以苹果现在已经发布了 2012 WWDC 开发者视频。他们有两个视频可以帮助您(需要开发者帐户):

  1. iOS 应用程序性能:响应性
  2. iOS 应用性能:图形和动画

我认为他们谈论的一件事可能会对您有所帮助,那就是使用以下方法:setNeedsDisplayInRect:(CGRect)rect. 使用此方法而不是普通方法setNeedsDisplay并确保您的drawRect方法仅绘制给定的矩形可以极大地提高性能。就个人而言,我使用以下功能:CGContextClipToRect(context, rect);将我的绘图仅剪辑到提供的矩形。

例如,我有一个单独的类,用于使用 Core Text 将文本直接绘制到我的视图中。我的 UIView 子类保留对这个对象的引用并使用它来绘制它的文本,而不是使用 UILabel。setNeedsDisplay我曾经在文本更改时刷新整个视图 ( )。现在我让我的 CoreText 对象计算更改后的 CGRect 并用于setNeedsDisplayInRect仅更改包含文本的视图部分。这确实有助于我在滚动时的表现。

于 2012-06-19T14:25:13.357 回答
1

我最终使用了@Kurt Revis对类似问题的回答中描述的方法。

我最小化了使用的层数,添加UIImageView并将其图像设置为UIImage包装我的CGImageRef. 请阅读上述答案以获取有关该方法的更多详细信息。

最后,我的应用程序变得比以前更简单,并且在 iPad 2 和 iPad 3 上以几乎相同的速度运行。

于 2012-06-22T05:21:58.207 回答