ios - Cocos-2d iOs-cclayer的不同内存释放行为

标签 ios objective-c memory-management cocos2d-iphone

我很确定我以某种方式犯了一个愚蠢的错误,但是我似乎无法解决它。我得到了CCScene的子类,该子类又将cclayer的子类作为一个层,大致如下所示:

@interface MyLayer : CCLayer {
}

// some methods

@end

@interface MyScene : CCScene {

    MyLayer *_myLayer;

}

// some methods

@end


在构造I时,请执行以下操作:

-(id) init {
    if (self = [super init]) {
        _myLayer = [MyLayer node];
        [self addChild:_myLayer];

        // more stuff
    }
}


我需要_myLayer中的引用,因为我需要与该图层进行交互。但是,这使我的保留计数为2(一次在_myLayer中,一次作为场景的子节点)。到目前为止没有问题-至少据我所知。但是,这也意味着我必须释放它。因此,MyScene的解除分配如下所示:

-(void) dealloc {

    [_myLayer release];
    _myLayer = nil;

    [super dealloc];

}


如果现在在运行时释放场景,则一切正常。我遵循发布的过程,这一切都很好,我可以毫无问题地发布包括场景在内的整个场景。调用一次MyScene :: release,在主动调用[_myLayer版本]时将保留计数降低一个,调用一次MyLayer :: release(通过[super dealloc]-删除CCNode中的所有子代),每个人都很高兴。

但是,一旦我玩完整个游戏(杀死设备上的应用程序)并调用CCDirector :: end,整个过程就会中断,因为实际上,它尝试释放_myLayer两次-一次使用显式调用,一次通过释放孩子。

现在我可以理解,如果在两种情况下都相同,我会犯某种错误,但事实并非如此。一旦按预期方式工作-在第一种情况下,将保留计数降低一个,然后在工作方式不同时再次释放它。而且我不知道为什么会这样。

我尝试一起废弃[_myLayer版本]。在这种情况下,_myLayer在运行时根本不会释放,但在关机期间一切正常。因此,这在一定程度上是一致的,但这并不能真正帮助我。

最佳答案

首先:retainCount is useless

这将返回MyLayer的自动发布实例:

_myLayer = [MyLayer node];


它等效于:

_myLayer = [[[MyLayer alloc] init] autorelease];


如果不使用其他代码而将其保留在该位置,则在init方法返回后的某个时间_myLayer会变成悬挂指针。下次自动清除池一定会清除,如果我记得正确的话,这会发生在cocos2d中的任何帧中。在ARC下,ivar本身默认情况下是强引用,因此该层将被保留,并且不会如您所愿地进行重新分配。因此,如果您不喜欢自动发布及其功能,请使用ARC。 ;)

然后,将图层添加为子图层,将其放入数组中,这意味着将保留该图层:

[self addChild:_myLayer];


因此,只要该层仍然是场景的子级,就不会取消分配。

现在,就像您之前的许多人一样,您正在寻找错误的位置以解决图层无法释放的问题。通过在dealloc中执行此操作,可以添加一个无关的发行版:

[_myLayer release];


现在,此操作暂时可以正常进行,因为实际问题是该层未释放,并且您在此处强制将其释放。但是,过了一段时间,场景的子数组将被释放,这会将释放发送到每个对象,然后由于过度释放图层而导致崩溃。

因此,您应该跟踪的实际问题是为什么该层不会取消分配。在这里,我感觉到更多的问题:


  我可以释放整个场景


如果那是您的意思是要向场景发送release消息,那是错误的。再次,这将过度释放场景。当您调用replaceScene或类似方法时,Cocos2d将清理场景。场景本身通常也自动发布,肯定是通过nodescene类方法创建的。

如果这不是您要执行的操作并且该图层没有释放,请检查是否有保留周期。或者,也许您只是希望各层在场景之前释放?不必一定要按此顺序进行,自动释放也不少。

您可以通过使两个(或多个)同级节点相互保持(即A层保留B层,B层保留A层)或同级保留其父级,例如_myLayer保留一个保留引用,来轻松地创建一个保留周期。到场景(顺便说一句与通过self.parent访问它相同)。

现在,我说要使用ARC,因为它可以使所有这些问题几乎立即消失,除了保留周期。但是对于保留周期,它提供了一种非常简单有效的解决方案:将弱引用归零。

例如,您可以在接口中将层引用声明为弱引用,而不必担心会保留该层:

 __weak MyLayer *_myLayer;


此外,释放图层时,_myLayer会自动设置为nil。发生这种情况的情况是该层不再具有更强的引用,而不是像autorelease那样在以后的某个时间。

积极的副作用是您现在可以安全地执行以下操作:

@interface LayerA : CCLayer
@property (weak) LayerB* layerB;
@end

@interface LayerB : CCLayer
@property (weak) LayerA* layerA;
@end


在MRC下,如果您相应地分配层并且在dealloc方法之前不将它们设置为nil,则这将创建一个保留周期。保留周期的棘手之处在于它们无法在dealloc方法中解析,因为根据定义,所有参与保留周期的对象都不会被解除分配。在cocos2d中通常这样做的地方是清理方法。

但是现在有了ARC,绝对没有问题。任一层或两层都将取消分配,因为另一层中的引用没有保留(弱),并且当任一层被释放时,该引用都设置为nil,因此不会发生任何崩溃。

我已经专门与ARC合作开发了两年。我从不回头。我与内存管理有关的错误配额几乎降至零,这很荒谬。关于我偶尔需要查看的唯一事情是,当我不希望弱引用为零时。通常,那是我错误地认为该对象在某处具有强引用,但实际上没有。

使用ARC可以节省大量时间,即使您真的真的非常想学习这个知识,您也最好真的真的真的对内存管理在过去的Objective-C开发中过去的工作方式非常感兴趣。就像我奶奶还在为第一部iPhone编写代码一样! :)

PS:当使用ARC时,您放弃对内存管理的控制是一个神话。您可以采取许多巧妙的技巧来重新获得控制权,即使是很短的时间。主要通过采用桥式铸造。因此,即使ARC可能需要花费更多的时间,并且可能会陷入一个紧密的循环中,您仍然可以使用MRC手动优化代码(如果需要;您可能永远不需要)。这超出了这个答案的范围(我已经走得太远了),但是这些选项确实存在,但是几乎不需要行使它们。

这就是为什么我大胆使用ARC的原因,因为不这样做是不负责任的。海事组织。

关于ios - Cocos-2d iOs-cclayer的不同内存释放行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19683806/

相关文章:

ios - 对 CAReplicatorLayer 实例应用不同的效果

iphone - 是否有任何工具可以浏览您应用程序的 iCloud 数据?

objective-c - 需要一些调查的 NSString

ios - 如何更改#define值

c# - 如何在 C#(托管代码)中获取 *THREAD* 的 CPU 使用率和/或 RAM 使用率?

c# - 使用预分配和池化的所有内存从线路转换为基本类型(int、decimal、Datetime ...)——如何绕过 System.String

ios - Apple Pay,我们可以禁用电子邮件、电话和地址编辑吗?

objective-c - 将 Twitter 帐户添加到 ABRecordRef(人员)的最简单方法

c - 将元数据添加到内存

ios - 无法在 UItableviewcell 中加载 collectionview