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

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.

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.

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.

Related

objective-c: Using NSManagedObject for saving data with CoreData

Is it possible to extend an derived class from NSManagedObject? I'm asking this because I tried to do it. My entity looks like this:
So this means a class similar to the following code should be generated:
#import <Foundation/Foundation.h>
#interface Player : NSManagedObject
#property (nonatomic, copy) NSNumber* orderNumber;
#property (nonatomic, copy) NSString *name;
#end
.m file
#import "Player.h"
#implementation Player
#dynamic name, orderNumber;
#end
This two variables are saved to the SQLite database.
Now since I need some additional variables for the player I just added them to the class. It still worked.
#import "Player.h"
#implementation Player
#dynamic name, orderNumber;
- (id) init
{
self = [super init];
if (self != nil)
{
[self reset];
}
return self;
}
#synthesize isStillInGame = _isStillInGame;
- (void) reset
{
_isStillInGame = TRUE;
}
- (void) setOutOfGame
{
_isStillInGame = FALSE;
}
#end
But now when I change the isStillInGame bool, all instances of the Player Class are changed. Is this normal or do I have an error in my code?
A second question I can't answer is, why I can't access the object variables while debugging. When I try to watch an Player instance variable I just see this:
Is it possible to see more?

Can't get an objective-c object to respond to a message. What am I missing?

I'm trying to teach myself Objective-C and as an exercise, I'm trying to write an app with one button and one label. When I click on the button, I want to trigger a calculation then see the results in the label. The following code compiles and runs with no errors or warnings but as far as I can tell, the [object method] 'call' doesn't do anything. I've spent hours on this and just don't see what's wrong. Can anyone explain the problem? Thanks.
*** testMethodViewController.h ****
#import <UIKit/UIKit.h>
#import "testBrain.h"
#interface testMethodViewController : UIViewController
{
IBOutlet UILabel *display;
testBrain *model;
}
- (IBAction)cellPressed:(UIButton *)sender;
#end
*** testMethodViewController.m ****
#import "testMethodViewController.h"
#implementation testMethodViewController
- (testBrain *)model
{
if (!model) {model = [[testBrain alloc] init];}
return model;
}
- (IBAction)cellPressed:(UIButton *)sender
{
int x = [model check:3]; //This method call doesn't work. But gets no errors.
NSLog(#"Results from model: %i", x); //Says x = 0, but I expect 6
NSString *xAsString = [NSString stringWithFormat: #"testBrain: %i", x];
display.text = xAsString; //Label is updated and displays: testBrain: 0
} //I expect: testBrain: 6
#end
*** testBrain.h ****
#import <Foundation/Foundation.h>
#interface testBrain : NSObject {}
- (int) check:(int) anInteger;
#end
*** testBrain.m ****
#import "testBrain.h"
#implementation testBrain
- (int) check:(int) anInteger //3 passed as the parameter.
{
int r = anInteger + anInteger;
NSLog(#"inside check %i", r); //Debugging line: doesn't print.
return r;
}
#end
When this code runs:
int x = [model check:3];
model is nil. In Objective-C, messages sent to nil silently do nothing, and return 0. So, as you see, x is 0 and -check: is never called.
Apparently you were expecting this method to be called automatically:
- (testBrain *)model
{
if (!model) {model = [[testBrain alloc] init];}
return model;
}
However, that method will be called only if you do it yourself, by saying [self model] or self.model. So, this line would fix it:
int x = [[self model] check:3];
Try it and see.
Going a little further: It would be clearer to remove the model method entirely, and create the instance variable model when the UIViewController is created. That way, we can guarantee that model is valid anytime any code in the testMethodViewController class runs.
You would do that by overriding UIViewController's designated initializer:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Now you can initialize your instance variables
model = [[testBrain alloc] init];
}
return self;
}
With your model method, you are halfway towards Lazy Instantiation, however to properly achieve this, you must always acess the lazily instantiated object through its accessor method. You aren't doing this in your button action, so your messages are going to nil, which is silently ignored.
This is one of the reasons you often see instance variables in objective-c declared with a leading or trailing underscore. If you then typed model anywhere in the rest of your class, it would be a compiler error, forcing you to use the accessor. Typically this is implemented with properties and the synthesize statement:
In your interface:
#property (nonatomic, strong) TestBrain* model;
In your implementation:
#synthesize model = model_;
Your model method would be:
-(TestBrain*)model
{
if (!model_)
model_ = [[TestBrain alloc] init];
return model_;
}
You would then use self.model instead of model throughout the rest of the class.
If you are just starting out, the Stanford iOS course on iTunes U is an excellent resource, a lot of this sort of material is covered.
int x = [model check:3];
This line should be:
int x = [self.model check:3];
you are almost there. You need to use #property and #synthesize in order to complete this. The #synthesize directive will direct the compiler to create the setters and getters for a particular property. The #synthesize directive tells the compiler that variable is a property. Properties allow you to use the dot syntax. i.e. self.model which will automatically the call the getter or setter method, depending on the context.
In your testMethodViewController.h file change it to look like this:
#interface testMethodViewController : UIViewController
{
IBOutlet UILabel *display;
testBrain *model;
}
#property (nonatomic,retain) testBrain *model;
- (IBAction)cellPressed:(UIButton *)sender;
#end
then in the .m implementation you need to use #synthesize after the #implementation. Like this:
#implementation testMethodViewController
#synthesize model; // tells the compiler to synthesize the setter and getter for you
- (testBrain *)model
{
if (!model) {model = [[testBrain alloc] init];}
return model;
}
then in your cellPressed: method, you need to use self.model in order for the getter to be called:
- (IBAction)cellPressed:(UIButton *)sender
{
int x = [self.model check:3]; //This method call doesn't work. But gets no errors.
NSLog(#"Results from model: %i", x); //Says x = 0, but I expect 6
NSString *xAsString = [NSString stringWithFormat: #"testBrain: %i", x];
display.text = xAsString; //Label is updated and displays: testBrain: 0
}
Hope this helps.
I dont see anywhere in the testMethodViewController.h file
IBOutlet UIButton *button;
Also check if u have properly connected all IBOutlet, IBAction & delegate, datasource.

Problems with Pass-by-Reference in ObjC

I wanna do pass by reference in ObjC.
Here is the illustration of the scenario:
// MainClass.h
#interface MainClass
{
Board *board;
OtherClass *other;
}
#end
// MainClass.m
#implementation MainClass
- (id)init
{
if (self = [super init])
{
board = [[Board alloc] init];
other = [[OtherClass alloc] initOtherClassWithBoard: board];
}
return self
}
#end
// Other class.h
#interface OtherClass
{
Board *refBoard;
}
- (void)initOtherClassWithBoard: (Board*)ref;
#end
#implementation OtherClass
- (id)initOtherClassWithBoard: (Board*)ref
{
if (self = [super init])
{
refBoard = ref;
}
return self;
}
#end
So my question is does my OtherClass is holding a reference Board from MainClass?
Or OtherClass's Board is a copy but not a reference.
If I wanna do a reference then how should I change it?
EDIT:
What if I add this on both classes
#property (nonatomic, retain) Board *board;
You always refer to objects in Objective-C via pointers, a.k.a. references. Therefore, objects are always passed by reference in Objective-C.
It's a reference. It won't be a copy unless you do something to make it a copy (like use #property (copy) or send it a copy message).

Help with a method that returns a value by running another object's method

I have a Class that runs the following method (a getter):
// the interface
#interface MyClass : NSObject{
NSNumber *myFloatValue;
}
- (double)myFloatValue;
- (void)setMyFloatValue:(float)floatInput;
#end
// the implementation
#implementation
- (MyClass *)init{
if (self = [super init]){
myFloatValue = [[NSNumber alloc] initWithFloat:3.14];
}
return self;
}
// I understand that NSNumbers are non-mutable objects and can't be
// used like variables.
// Hence I decided to make make the getter's implementation like this
- (double)myFloatValue{
return [myFloatValue floatValue];
}
- (void)setMyFloatValue:(float)floatInput{
if ([self myFloatValue] != floatInput){
[myFloatValue release];
myFloatValue = [[NSNumber alloc] initWithFloat:floatInput;
}
#end
When I mouse over the myFloatValue object during debugging, it does not contain a value. Instead it says: "out of scope".
I would like to be able to make this work without using #property, using something other than NSNumbers, or other major changes since I just want to understand the concepts first. Most importantly, I would like to know what mistake I've apparently made.
I can see a couple of mistakes:
The line #implementation should read #implementation MyClass
The function setMyFloatValue is missing a closing ] and } —it should read:
- (void)setMyFloatValue:(float)floatInput{
if ([self myFloatValue] != floatInput){
[myFloatValue release];
myFloatValue = [[NSNumber alloc] initWithFloat:floatInput];
}
}
I've just tested it in Xcode and it works for me with these changes.
Why not just set property in interface and synthesize accessors in implementation?
#interface MyClass : NSObject {
float *myFloat
}
#property (assign) float myFloat;
#end
#implementation MyClass
#synthesize myFloat;
#end

Why can't I initialize a class through a setter?

If I have a custom class called Tires:
#import <Foundation/Foundation.h>
#interface Tires : NSObject {
#private
NSString *brand;
int size;
}
#property (nonatomic,copy) NSString *brand;
#property int size;
- (id)init;
- (void)dealloc;
#end
=============================================
#import "Tires.h"
#implementation Tires
#synthesize brand, size;
- (id)init {
if (self = [super init]) {
[self setBrand:[[NSString alloc] initWithString:#""]];
[self setSize:0];
}
return self;
}
- (void)dealloc {
[super dealloc];
[brand release];
}
#end
And I synthesize a setter and getter in my View Controller:
#import <UIKit/UIKit.h>
#import "Tires.h"
#interface testViewController : UIViewController {
Tires *frontLeft, *frontRight, *backleft, *backRight;
}
#property (nonatomic,copy) Tires *frontLeft, *frontRight, *backleft, *backRight;
#end
====================================
#import "testViewController.h"
#implementation testViewController
#synthesize frontLeft, frontRight, backleft, backRight;
- (void)viewDidLoad {
[super viewDidLoad];
[self setFrontLeft:[[Tires alloc] init]];
}
- (void)dealloc {
[super dealloc];
}
#end
It dies after [self setFrontLeft:[[Tires alloc] init]] comes back. It compiles just fine and when I run the debugger it actually gets all the way through the init method on Tires, but once it comes back it just dies and the view never appears. However if I change the viewDidLoad method to:
- (void)viewDidLoad {
[super viewDidLoad];
frontLeft = [[Tires alloc] init];
}
It works just fine. I could just ditch the setter and access the frontLeft variable directly, but I was under the impression I should use setters and getters as much as possible and logically it seems like the setFrontLeft method should work.
This brings up an additional question that my coworkers keep asking in these regards (we are all new to Objective-C); why use a setter and getter at all if you are in the same class as those setters and getters.
You have declared frontLeft as a 'copy' property:
#property (nonatomic,copy) Tires *frontLeft, *frontRight, *backleft, *backRight;
When you assign to this property, a copy is made by invoking the object's copy method. This only works for objects which support the NSCopying protocol (i.e., which implement a copyWithZone: method). Since your Tires class does not implement this method, you get an exception.
You probably want to change this to be a 'retain' property:
#property (nonatomic,retain) Tires *frontLeft, *frontRight, *backleft, *backRight;
See the Objective C documentation on declared properties for more on property declarations.
One problem that i see is here:
- (void)viewDidLoad {
[super viewDidLoad];
[self setFrontLeft:[[Tires alloc] init]];
}
When you call [Tires alloc] you get back an object with a retain count of 1. You then use a set method which you have synthesized, which bumps the retain count to 2. When your object is done with the Tire object, it will reduce the retain count back to 1, but the tire will never get deallocated. I think you should use:
[self setFrontLeft:[[[Tires alloc] init] autorelease]];