lsmpascal
lsmpascal

Reputation: 780

Objective-C : Calling a property of a class in another

I'm using Cocos2d and I can not access properties of an object from another one. Here I just want to get the hitpoints of a ship form a scene. It fails and returns an error : uncaught exception 'NSInvalidArgumentException', reason: '-[CCSprite hitpoints]: unrecognized selector...

As hitpoints is declared in the interface of the class Ship I can't figure out why. The only thing I understand is that it's a inhéritance issue.

Let's show some code :

Ship.h

#import <Foundation/Foundation.h>
#import "cocos2d.h"

@interface Ship : CCSprite {
    int hitpoints;
}

@property (nonatomic, assign) int hitpoints;

- (id)init;

@end

Then Ship.m

#import "Ship.h"

@implementation Ship

@synthesize hitpoints;

- (id)init
{
    hitpoints = 3;
    self = [CCSprite spriteWithImageNamed:@"ship.png"];
    return self;
}
@end

In the Scene.m

#import "Ship.h"
@implementation Scene
{
    Ship *_player;
}

- (id)init
{
    _player = [[Ship alloc] init];

    [self addChild:_player];

    // ERROR HERE
    NSLog(@"%s = %d", "_player hp", [_player hitpoints]);
}

Thank you.

Upvotes: 0

Views: 156

Answers (2)

Tommy
Tommy

Reputation: 100622

This:

- (id)init

Means "a method that returns any kind of object, which is called init".

This:

self = [CCSprite spriteWithImageNamed:@"ship.png"];

Means "create an instance of CCSprite". You then return that instance.

So _player ends up being an instance of CCSprite. CCSprite does not implement hitpoints so the exception is raised.

What are you actually trying to achieve? A subclass of CCSprite? trojanfoe has covered how to write a proper init for that. Things I think you need to know:

  • all classes look the same at runtime;
  • declaring the type of class pointers helps humans and the compiler to check your code but doesn't change the code generated;
  • some Objective-C patterns (probably most notably including class clusters) are built around init being able to return any kind of class — it's a useful feature, not a misfeature.

Upvotes: 1

trojanfoe
trojanfoe

Reputation: 122391

I suspect the issue is with your init method; you shouldn't be accessing properties until the object is initialised and you should be calling [super initWith...] instead of the class creation method.

I would suggest the following changes:

Ship.h:

@interface Ship : CCSprite

@property (nonatomic, assign) int hitpoints;

@end

Ship.m:

#import "Ship.h"

@implementation Ship

@synthesize hitpoints;

- (id)init
{
    self = [super initWithImageNamed:@"ship.png"];
    if (self) {
        self.hitpoints = 3;
    }
    return self;
}
@end

Always use object.property when referencing a property, even when object == self.

Upvotes: 2

Related Questions