Ell*_*rry 12 iphone objective-c uiview ios4 ios
为什么在iOS 4.3.5上,"大"(960 x 1380)自定义UIView执行CABackingStoreUpdate效率低下,如何提高绘图操作的性能?
不完全确定我的意思?继续阅读......
随着我对这个问题的理解的发展,这个问题也是如此.因此,问题本身是相似的,但自从首次提出问题以来,以下文本正文中的代码示例和基本细节/推理已经发生了重大变化.
我有一个令人难以置信的基本应用程序(底部的代码),它在自定义UIView的drawRect:方法中绘制单个elipses.当绘制的elipses的大小保持不变但自定义UIView的大小变大时,应用程序演示了性能上的差异:
我在运行iOS 4.3.5的iPod 4th Gen和运行iOS 5.1.1的iPad 1st Gen上运行应用程序,使用不同大小的自定义UIViews一系列.
下表显示了时间分析器仪器的结果:
以下仪器跟踪显示每个设备的两个极端的详细信息:
iOS 5.1.1 - (自定义UIView大小320 x 460)
iOS 5.1.1 - (自定义UIView大小960 x 1380)
iOS 4.3.5 - (自定义UIView大小320 x 460)
iOS 4.3.5 - (自定义UIView大小960 x 1380)
正如你所能(希望)在4个案例中的3个案例中我们得到了我们所期望的:大部分时间用于执行自定义UIViews drawRect:方法并且每个都持有10fps.
但是第四种情况显示了性能的下降,应用程序在仅绘制单个形状时难以保持7fps.在UIView的CALayer显示方法中,大部分时间用于复制内存,具体如下:
[CALayer display]>
[CALayer
_display ]> CABackingStoreUpdate>
CA :: Render :: ShmemBitmap :: copy_pixels(CA :: Render :: ShmemBitmap const*,CGSRegionObject*)>
memcpy $ VARIANT $ CortexA8
现在从这些数字中看出这里出现严重错误并不是天才.使用大小为960 x 1380的自定义UIView,iOS 4.3.5花费的时间超过复制内存的4倍,而不是绘制整个视图的内容.
现在,根据上下文,我再次提出我的问题:
为什么在iOS 4.3.5上,"大"(960 x 1380)自定义UIView执行CABackingStoreUpdate效率低下,如何提高绘图操作的性能?
很感谢任何形式的帮助.
我也在Apple Developer论坛上发布了这个问题.
现在,显然,为了这个问题,我已经将我的真正问题简化为最简单的可重现案例.我实际上试图为位于UIScrollView内的960 x 1380自定义UIView的一部分设置动画.
虽然我很欣赏任何人在未通过Quartz 2D达到他们想要的性能水平时引导任何人走向OpenGL ES的诱惑,但我要求任何采用这种方式的人至少提供一个解释,说明为什么Quartz 2D难以表现甚至是iOS 4.3.1中最基本的绘图操作,其中iOS 5.1.1没有问题.你可以想象我对为这个基石案重写一切的想法并不感到兴奋.这也适用于建议使用Core Animation的人.虽然为了简单起见,我在演示中使用了改变颜色的elipses(一个非常适合Core Animation的任务),但我实际想要执行的绘制操作是随着时间的推移而扩展的大量线条,一个绘图任务Quartz 2D非常适合(当它具有高性能时!).此外,再次,这将需要重写,并没有帮助解释这个奇怪的性能问题.
TViewController.m(标准视图控制器的实现)
#import "TViewController.h"
#import "TCustomView.h"
// VERSION 1 features the custom UIView the same size as the screen.
// VERSION 2 features the custom UIView nine times the size of the screen.
#define VERSION 2
@interface TViewController ()
@property (strong, nonatomic) TCustomView *customView;
@property (strong, nonatomic) NSTimer *animationTimer;
@end
@implementation TViewController
- (void)viewDidLoad
{
// Custom subview.
TCustomView *customView = [[TCustomView alloc] init];
customView.backgroundColor = [UIColor whiteColor];
#if VERSION == 1
customView.frame = CGRectMake(0.0f, 0.0f, 320.0f, 460.0f);
#else
customView.frame = CGRectMake(0.0f, 0.0f, 960.0f, 1380.0f);
#endif
[self.view addSubview:customView];
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
[customView addGestureRecognizer:singleTap];
self.customView = customView;
}
#pragma mark - Timer Loop
- (void)handleTap:(UITapGestureRecognizer *)tapGesture
{
self.customView.value = 0.0f;
if (!self.animationTimer || !self.animationTimer.isValid) {
self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(animationLoop) userInfo:nil repeats:YES];
}
}
#pragma mark - Timer Loop
- (void)animationLoop
{
// Update model here. For simplicity, increment a single value.
self.customView.value += 0.01f;
if (self.customView.value >= 1.0f)
{
self.customView.value = 1.0f;
[self.animationTimer invalidate];
}
[self.customView setNeedsDisplayInRect:CGRectMake(0.0f, 0.0f, 320.0f, 460.0f)];
}
@end
Run Code Online (Sandbox Code Playgroud)
-
TCustomView.h(自定义视图标题)
#import <UIKit/UIKit.h>
@interface TCustomView : UIView
@property (assign) CGFloat value;
@end
Run Code Online (Sandbox Code Playgroud)
-
TCustomView.m(自定义视图实现)
#import "TCustomView.h"
@implementation TCustomView
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
// Draw ellipses.
CGContextSetRGBFillColor(context, self.value, self.value, self.value, 1.0f);
CGContextFillEllipseInRect(context, rect);
// Draw value itself.
[[UIColor redColor] set];
NSString *value = [NSString stringWithFormat:@"%f", self.value];
[value drawAtPoint:rect.origin withFont:[UIFont fontWithName:@"Arial" size:15.0f]];
}
@end
Run Code Online (Sandbox Code Playgroud)
由于iPod Touch第4代和iPad第1代都有类似的硬件(相同数量的内存/相同的GPU),它表明你看到的问题是由于iOS4中未优化的代码路径.
如果你看一下在iOS4上导致(负面)性能峰值的视图大小,它们都有一个长于1024的边.最初1024x1024是UIView的最大尺寸,虽然这个限制已被取消,但它完全是可能大于此的视图仅在iOS5及更高版本中变得高效.
我猜想你在iOS4中看到的多余内存复制是由于UIKit为大型UIView使用全尺寸内存缓冲区,但是在它们可以合成之前必须复制适当大小的内存块.而且在iOS5及更高版本中,他们要么删除了可以合成的图块大小的任何限制,要么改变了UIKit为这样大的UIViews呈现的方式.
在iOS4上解决这个瓶颈问题时,您可以尝试使用较小的UIViews来平铺您想要覆盖的区域.如果您将其构造为:
Parent View - contains drawing and event related code Tile View 1 - contains drawRect ... Tile View n - contains drawRect
在每个切片视图中,您可以在调整图形上下文的变换后适当地要求父视图呈现其内容.这意味着您不必更改绘图代码,它只会被多次调用(这需要很小的开销,但请记住每次调用只会绘制整个视图的一部分).
请注意,父视图没有drawRect方法很重要 - 否则UIKit会认为你想直接绘制它,它会创建一个后备存储,从而使你回到相同的情况.
您还可以查看CATiledLayer - 这会为您提供平铺但异步; 意味着您的绘图代码必须处理从一个或多个后台线程执行.
归档时间: |
|
查看次数: |
1752 次 |
最近记录: |