有没有理由我不能在UIView类之外初始化CALayer?

时间:2009-04-30 03:43:35

标签: iphone uiview calayer

我一直在尝试将CALayers作为我正在研发的iPhone应用程序中的“精灵”。从我一直在阅读的内容来看,这似乎是正确的事情。

当我在UIView类中设置图层时,我可以让图层显示在屏幕上:     NSLog(@“GameView initWithCoder”);

NSString* imagePath = [[NSBundle mainBundle] pathForResource:@"earth" ofType:@"png"];
earthImage = [[UIImage alloc] initWithContentsOfFile:imagePath];

earthLayer = [CALayer layer];
[earthLayer setContents:(id)[earthImage CGImage]];
[earthLayer setBounds:CGRectMake(0, 0, 32, 32)];
[earthLayer setPosition:CGPointMake(50, 50)];
[earthLayer setName:@"earth"];

[[self layer] addSublayer:earthLayer];

但是,当我尝试在UIView之外初始化CALayer时,我的应用程序崩溃了:

- (void)loadImage:(NSString*)fileName
{
  NSLog(@"GamePiece loadImage");
  NSArray* fileNameParts = [fileName componentsSeparatedByString:@"."];
  imageName = [[fileNameParts objectAtIndex:0] retain];
  NSString* ext = [fileNameParts objectAtIndex:1];

  NSString* imagePath = [[NSBundle mainBundle] pathForResource:imageName ofType:ext];
  image = [[UIImage alloc] initWithContentsOfFile:imagePath];

  layer = [CALayer layer];
  [layer setContents:(id)[image CGImage]];

  [layer setBounds:CGRectMake(0.0f, 0.0f, 32.0f, 32.0f)];
  [layer setPosition:CGPointMake(50.0f, 50.0f)];
  [layer setName:imageName];

  [fileNameParts release], fileNameParts = nil;
  [ext release], ext = nil;
}

NSLog(@"GameView initWithCoder");
earth = [[GamePiece alloc] init];
[earth loadImage:@"earth.png"];

[[self layer] addSublayer:[earth layer]];

显然我在这里做错了,但我不知道那会是什么。我已经阅读了“核心动画编程指南”和“iPhone应用程序编程指南”,但他们似乎并没有谈到如何初始化CALayer而不是传递。

我只需要在正确的方向上轻轻推动。在编程iPhone时,我对其他所有学到的东西都有了很好的理解。我在图形,图层和动画方面遇到了一些问题。

提前致谢!

3 个答案:

答案 0 :(得分:2)

您不拥有从layer方法获得的图层,因此您必须拥有所有权。在您的方法返回之后的某个时间,该层正在被释放,这导致您的崩溃。解决这个问题:

layer = [[CALayer layer] retain]; // now we explicitly take ownership of this layer

另外,不要忘记现在拥有它,因为它已经拥有了它:

// in your dealloc method
[layer release];

您应该查看Memory Management Programming Guide for Cocoa以获取有关何时拥有对象的更多详细信息以及您对内存管理的责任。

答案 1 :(得分:0)

杰森,非常感谢你的回应!但是你的回答似乎对我的问题的结果没有任何影响。

实际上,答案“是”与内存管理相关的东西。不是Layer,而是在调用componentsSeparatedByString后从NSArray获得的名称。如果我不释放NSArray,代码工作正常。所以我需要在数组中复制NSString而不是使用数组中的对象。

我不得不说我没有按照你的建议阅读Cocoa内存管理编程指南。但是,我已经阅读了其他几本书,其中讨论了Cocoa中用于创建对象的标准及其保留计数。我有一个公平的理解,但显然,还没有那么好。

我真的怀疑我应该将字符串复制出数组,但是当它在UIView中工作时,我只是认为它是正确的。问题是,我没有保持它像我在GamePiece对象中。

感谢您至少让我朝着正确的方向前进!我真的很擅长这段代码。

答案 2 :(得分:0)

要做你想做的事,你可以在这里改变你的代码。在loadImage:中使用此行将您加载的UIImage添加到earth图层中:

  

self.contents = image.CGImage;

CALayers具有contents属性,您可以将其设置为CGImage,而不是经历使用Core Graphics调用将图像绘制到图层上下文中的麻烦。

然后在父UIView子类中,您可以将earth添加到视图层次结构中,如下所示:

  

[self.layer addSublayer:earth];

请注意,earth本身是CALayer,其中包含图像作为内容,因此我将其直接添加为子图层。我总是忘记做的另一件事是设置图层的frame属性。否则它的宽度和高度将为零,因此不会显示。

在您的代码中,您创建了一个图层,但从未将图层添加到视图层次结构中。如果视图(类UIView)及其子视图有一棵树。树的根是[UIWindow mainwindow](请注意,UIWindow是UIView的子类。每个UIView都有layer类型CALayer的属性,它实际上保持其可见内容。UIView只是CALayer的一个包装,可以添加触摸处理功能等内容。

你在这里缺少的重要想法是,对于屏幕上可见的任何内容,它必须是这个视图树的一部分,以UIWindow为根,并且所有视图必须是可见的(即具有它们的{{ 1}}属性设置为hidden)在顶层窗口和相关图层之间。只有这样才能在屏幕上看到一个图层。因此,在代码中的某个位置,您需要通过调用NO将图层添加为已经可见的内容的子图层。

在您的情况下,我假设addSublayer:earth的一个实例,您在CALayer方法中所做的是创建一个新的loadImage:(在CALayer图层内调用[CALayer layer]},但之后您从未将新图层添加到任何内容中,因此它永远不会显示。您所做的是尝试将earth图层添加到我认为是earth图层

的图层中,该图层仍然没有任何内容
  

[[self layer] addSublayer:[earth layer]];

这一行的一个问题是您似乎认为UIViewlayer的属性。实际上earthlayer的类方法,它是一种创建CALayer新实例的工厂方法。这很令人困惑,因为CALayer实际上正在访问[self layer]实例的图层属性。 (我假设UIView指的是self子类的实例。)