提前释放自动释放对象?

发布于 2024-12-10 14:09:27 字数 2199 浏览 0 评论 0原文

我有一个名为 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

因此,Ship 类看起来像这样:

@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 初始化 < strong>Ship 很好,继续修改 Ship 内的 CCSprite,在模拟器中,一切都很好。当在设备上运行它时,我们会获得加速度计输入。在加速度计方法中,第一行使程序崩溃,因为船不再存在。如果我在调试器中在加速度计方法的第一行设置断点,我会看到以下内容: self = (游戏玩法 *)
-> Ship = (Ship *) 0x004701e0

如果我继续运行程序,我会收到以下消息: 消息发送到已释放实例 0x4701e0

但我不确定为什么它被释放......大概是 0x004701e0 == 0x4701e0 ?

非常感谢, 本

I've got a class called GamePlay, which looks somewhat like this:

@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

So, the Ship class looks something like this:

@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 initialises the Ship fine and continues to modify the CCSprite within Ship, and in the simulator, all is well. When it comes to running it on a device, we get accelerometer input. In the accelerometer method, the first line crashed the program because the ship no longer exists. If I look in the debugger with a breakpoint on the first line of the accelerometer method I see the following:
self = (GamePlay *)
-> ship = (Ship *) 0x004701e0

If I then continue the running of the program, I get this message:
message sent to deallocated instance 0x4701e0

But I'm not sure why it's been deallocated...And presumably 0x004701e0 == 0x4701e0 ?

Many thanks,
Ben

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

花开柳相依 2024-12-17 14:09:27

您必须在 -[GamePlay init]保留ship(然后在dealloc中释放它),当然)。

You have to retain the ship in -[GamePlay init] (and later release it in dealloc, of course).

南笙 2024-12-17 14:09:27

解释这个问题:

  • GamePlay 类将 Ship 类实例初始化为自动释放
  • Ship 类初始化一个 sprite 类并将其作为子级添加到parentNode(GamePlay 类),它保留 sprite 但不保留 Ship 类本身

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

如果 Ship 仅从 NSObject 派生,则最终得到的结果有点像“损坏的”节点层次结构:

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

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

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

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

总体而言,这使您的节点层次结构更深一些,但更易于管理。您可以假设所有对象都派生自公共基类 (CCNode),并且您可以依靠 Cocos2D 在正确的时间以正确的顺序取消分配节点层次结构。

To explain the problem:

  • GamePlay class initializes Ship class instance as autorelease
  • Ship class initializes a sprite class and adds it as children to parentNode (GamePlay class), which retains the sprite but not the Ship class itself

Ole is correct that the Ship needs a retain in the code you posted. Personally I see a design problem here. Your Ship class should derive from CCNode, and you should add the Ship to the node hierarchy via addChild.

If Ship derives just from NSObject, what you end up with is sort of like a "broken" node hierarchy:

  • Cocos2D GamePlay class (ie CCLayer)
  • NSObject Ship class
  • Cocos2D CCSprite instance contained in Ship class, but added to GamePlay class

The problem with that is that the Ship class' sprite is "managed" by the Ship class but retained by the GamePlay class. That means you're responsible for retaining the non-CCNode classes like Ship yourself. The following makes for a simpler and more Cocos2D conform design, because it relies on Cocos2D managing the entire node hierarchy:

  • Cocos2D GamePlay class (ie CCLayer)
  • Cocos2D Ship class (derived from CCNode)
  • Cocos2D CCSprite instance contained in Ship class, added to Ship class as child

That way you would add the Ship class as child to the GamePlay class. The Ship class adds its CCSprite instance to self (the Ship class). You can (and should) get rid of the parentObject passed in the shipWithParentNode method. In Cocos2D it is quite dangerous to pass an instance of a node to another node, you might be tempted to retain that, which can lead to leaking the entire scene because of circular retain dependencies (a retains b, b retains a, both can't let go).

Overall this makes your node hierarchy a bit deeper but it is much easier to manage. You can assume all objects to derive from a common base class (CCNode) and you can rely on Cocos2D deallocating the node hierarchy in the right order at the right times.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文