有没有理由我不能在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时,我对其他所有学到的东西都有了很好的理解。我在图形,图层和动画方面遇到了一些问题。
提前致谢!
解决方案
您不拥有从 layer
方法获得的图层,因此您必须拥有所有权。在您的方法返回之后的某个时间,该层正在被释放,这导致您的崩溃。解决这个问题:
layer = [[CALayer layer] retain]; // now we explicitly take ownership of this layer
另外,不要忘记现在拥有它,因为它已经拥有了它:
// in your dealloc method
[layer release];
您应该查看 Cocoa内存管理编程指南有关何时拥有对象的更多详细信息以及您对内存管理的责任。
其他提示
实际上,答案“是”。与内存管理有关的东西。不是Layer,而是在调用componentsSeparatedByString后从NSArray获得的名称。如果我不释放NSArray,代码工作正常。所以我需要在数组中复制NSString而不是使用数组中的对象。
我不得不说我没有按照你的建议阅读Cocoa内存管理编程指南。但是,我已经阅读了其他几本书,其中讨论了Cocoa中用于创建对象的标准及其保留计数。我有一个公平的理解,但显然,还没有那么好。
我真的怀疑我应该将字符串复制出数组,但是当它在UIView中工作时,我只是认为它是正确的。问题是,我没有保持它像我在GamePiece对象中。
感谢您至少让我朝着正确的方向前进!我真的很擅长这段代码。
要做你想做的事,你可以在这里改变你的代码。在 loadImage:
中使用此行添加您加载到 earth
层的 UIImage
:
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作为其根,并且所有视图必须是可见的(即具有它们的<代码在顶层窗口和相关图层之间设置了 NO
。只有这样才能在屏幕上看到一个图层。因此,在代码中的某处,您需要通过调用 addSublayer:
将您的图层添加为已经可见的内容的子图层。
在你的情况下,我假设 earth
是 CALayer
的一个实例,你在 loadImage:
方法中所做的是在 earth
层中创建一个新的 CALayer
(通过调用 [CALayer layer]
)但是之后你从未将新图层添加到任何内容中它从未变得可见。你所做的是尝试添加 earth
层,它仍然没有任何内容,我假设是 UIView
的图层
[[self layer] addSublayer:[earth layer]];
这一行的一个问题是你似乎在想 layer
是 earth
的属性。实际上 layer
是 CALayer
的类方法,它是一种工厂方法,用于创建 CALayer
的新实例。这很令人困惑,因为 [self layer]
实际上正在访问 UIView
实例的layer属性。 (我假设 self
指的是 UIView
子类的一个实例。)