将autorelease对象解除分配到早期?

时间:2011-10-16 10:37:40

标签: objective-c cocoa-touch cocos2d-iphone

我有一个名为 GamePlay 的课程,看起来有点像这样:

@implementation GamePlay
-(id)init{
    if((self = [super init])){
        self.isAccelerometerEnabled = YES;

        //ship defined in header
        ship = [Ship shipWithParentNode:self];
        CGSize screenSize = [[CCDirector sharedDirector] winSize];
        float imageHeight = [ship spriteContentSize].height;
        [ship setSpritePosition:CGPointMake(screenSize.width*0.5, imageHeight*0.5)];
    }
    return self;
}

-(void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration{
    CGPoint pos = [ship spritePosition];
    pos.x = acceleration.x * 10;
    [ship setSpritePosition:pos];
}
@end

因此,发货类看起来像这样:

@implementation Ship

+(id)shipWithParentNode:(CCNode *)parentNode{
    return [[[Ship alloc] initWithParentNode:parentNode] autorelease];
}

-(id)initWithParentNode:(CCNode *)parentNode{
    if((self = [super init])){
        sprite = [CCSprite spriteWithFile:@"ship.png"];
        SpriteLoader *spriteLoader = [SpriteLoader sharedSpriteLoader];
        [spriteLoader addTextureAtlas:@"Spites_default.plist"];
        CCAnimation *spriteAnimation = [CCAnimation animationWithFrames:[spriteLoader getSpriteFramesWithName:@"ship-" andAmount:[NSNumber numberWithInt:5]] delay:0.08f];
        CCAnimate *anim = [CCAnimate actionWithAnimation:spriteAnimation];
        CCRepeatForever *repeat = [CCRepeatForever actionWithAction:anim];
        [sprite runAction:repeat];
        [parentNode addChild:sprite z:0 tag:SHIP];
    }
    return self;
}

-(void)setSpritePosition:(CGPoint)point{
    sprite.position = point;
}

-(CGPoint)spritePosition{
    return sprite.position;
}

-(CGSize)spriteContentSize{
    return sprite.contentSize;
}
@end

GamePlay 初始化发货并继续修改发货中的CCSprite,并且在模拟器中,一切正常。当在设备上运行它时,我们得到加速度计输入。在加速度计方法中,第一行因程序不再存在而导致程序崩溃。如果我在加速度计方法的第一行查看带有断点的调试器,我会看到以下内容: self =(GamePlay *)
- > ship =(Ship *)0x004701e0

如果我继续运行该程序,我会收到以下消息: 消息发送到解除分配的实例0x4701e0

但是我不确定为什么它被解除分配......大概是0x004701e0 == 0x4701e0?

非常感谢, 本

2 个答案:

答案 0 :(得分:4)

您必须retain ship -[GamePlay init] {以后release dealloc {。}}。

答案 1 :(得分:3)

解释问题:

  • GamePlay类将Ship类实例初始化为autorelease
  • Ship类初始化一个精灵类并将其作为子节点添加到parentNode(GamePlay类),后者保留精灵但不保留Ship类本身

Ole是正确的,因为Ship需要在您发布的代码中保留。我个人认为这里存在设计问题。您的Ship类应该来自CCNode,您应该通过addChild将Ship添加到节点层次结构中。

如果Ship仅从NSObject派生,那么你最终得到的就像是一个“破碎的”节点层次结构:

  • Cocos2D GamePlay类(即CCLayer)
  • NSObject Ship class
  • Cocos2D CCSprite实例包含在Ship类中,但已添加到GamePlay类

问题在于Ship类的sprite由Ship类“管理”,但由GamePlay类保留。这意味着您有责任保留非自己的CCNode类。以下是一个更简单,更符合Cocos2D的设计,因为它依赖于Cocos2D来管理整个节点层次结构:

  • Cocos2D GamePlay类(即CCLayer)
  • Cocos2D Ship类(源自CCNode)
  • Ship类中包含的Cocos2D CCSprite实例,作为子级
  • 添加到Ship类中

这样你就可以将Ship类作为子类添加到GamePlay类中。 Ship类将其CCSprite实例添加到self(Ship类)。您可以(并且应该)删除shipWithParentNode方法中传递的parentObject。在Cocos2D中将一个节点的实例传递给另一个节点是非常危险的,你可能会想要保留它,这可能会因为循环保留依赖性而导致整个场景泄漏(保留b,b保留a,两者都可以'放手)。

总的来说,这会使您的节点层次结构更深一些,但更容易管理。您可以假设所有对象都从公共基类(CCNode)派生,您可以依赖Cocos2D在正确的时间以正确的顺序释放节点层次结构。