Properties and pointers - objective-c

In Objective-C, I can't seem to be able to make a pointer to a property. For example:
I make a Class called Car which is a SubClass of SKSpriteNode.
One of the SKSpriteNode's properties is its position (a CGPoint).
Now for my Car class, I want to create 4 new properties called wheelPosition1, wheelPosition2, wheelPosition3, wheelPosition4 (which all are CGPoints)
Each of the wheelPosition properties are related to the position property of the Car (as the position property of the car will represent the centre whilst the wheels go out 1,1 in 4 directions) like this:
I want the wheelPosition properties to point to the address of the position property of the car whenever retrieving or setting their values...
Xcode doesn't let me do this, because the .position property is "temporary" - So I can't do something like:
CGPoint * x = &sprite.position;
In this example, the obvious solution would just to create a new Tyre class and create a node tree with 4 tyres... but this was only an example to illustrate my problem. This is just a generic problem I have found whilst programming - I can't get a variable to ever point to the property of another Object.
Right now I'm having to change my setter methods in the class to get around this which means that if I wanted to move the centre of the car somewhere else, it would be harder...
But is there another way which'll allow me to point to that property of the class?
At the request of a below comment here is the code:
#interface SomeClass : SKSpriteNode
#property CGPoint newNameForPositionProperty;
-(void) newPositionToPosition
#end
#implementation SomeClass
#synthesize newNameForPositionProperty;
-(void) newPositionToPosition {
newNameForPositionProperty = self.position;
}
#end
#implementation GameScene
-(void)didMoveToView:(SKView *)view {
SomeClass *newObject = [[SomeClass alloc] init];
newObject.position = CGPointMake(100, 100 * sqrt(3.0));
[newObject newPositionToPosition];
NSLog(#"Original Position Property: (%f, %f)", newObject.position.x, newObject.position.y);
NSLog(#"New Position Property: (%f, %f)", newObject.newNameForPositionProperty.x, newObject.newNameForPositionProperty.y);
//Change Position
NSLog(#" ");
//
newObject.position = CGPointMake(100, 100);
NSLog(#"Original Position Property: (%f, %f)", newObject.position.x, newObject.position.y);
NSLog(#"New Position Property: (%f, %f)", newObject.newNameForPositionProperty.x, newObject.newNameForPositionProperty.y);
}
#end
Here is an example of how the code might be applied:
#interface SomeClass : SKSpriteNode
#property double modulus, argument;
-(void) positionToModulusArgument;
#end
#implementation SomeClass
#synthesize modulus, argument;
-(void) positionToModulusArgument {
modulus = sqrt(pow(self.position.x, 2) + pow(self.position.y, 2));
//Realistically I would split the argument into 4 cases to represent the 4 quadrants, but as this is only an example, I will assume that the position is only in the top right quadrant.
argument = atan(self.position.y / self.position.x);
}
#end
#implementation GameScene
-(void)didMoveToView:(SKView *)view {
SomeClass *newObject = [[SomeClass alloc] init];
newObject.position = CGPointMake(100, 100 * sqrt(3.0));
[newObject positionToModulusArgument];
NSLog(#"Cartesian Form: (%f, %f)", newObject.position.x, newObject.position.y);
NSLog(#"Polar Form: (%f, %f)",newObject.modulus, newObject.argument);
//Change Position
NSLog(#" ");
//
newObject.position = CGPointMake(100, 100);
NSLog(#"Cartesian Form: (%f, %f)", newObject.position.x, newObject.position.y);
NSLog(#"Polar Form: (%f, %f)",newObject.modulus, newObject.argument);
}
#end
In either case, the property doesn't change when the other does.

Your approach is wrong. You do not need a pointer. Simply calculate the wheel positions from the (center) position using self.position. You need the indirection of pointers, if the property that should be use can change (not its value, but the property itself), i. e. from position to anotherPosition.
However, you do not apply the address operator to the property's ivar, but to the return value of the getter -position. Return values are temporary. If you want to have a pointer to the ivar itself, use &_position.
But as I said: Don't do this at all.

Related

How to get Class Objects to automatically display behaviour

I'm having trouble understanding a finer point of Writing and calling Classes. It's probably
easlier to grasp in Swift but it bothers me to start that study without
getting it right in obj_c first. Currently I do everything in the
ViewControllers with iVars and Globals. With two apps 18 months in the App
store its overdue to put them right.
I've formed a notion that properties are the Object's State, and any methods
within determine the Objects Behaviour but so far no-one is able to tell me.
here be a typical Class header:
#interface Math : NSObject
#property (nonatomic, assign) int a;
#property (nonatomic, assign) int b;
#property (nonatomic, assign) int c;
-(int)mathemagic:(int)a adding:(int)b;
#end
and the corresponding Class implementation:
#implementation Math
#synthesize a = _a;
#synthesize b = _b;
#synthesize c = _c;
- (instancetype)init {
self = [super init];
if (self) {
_a = 0;
_b = 0;
_c = 0;
}
return self;
}
-(int)mathemagic:(int)a adding:(int)b {
_c = (a + b);
return _c;
}
#end
and finally in the appropriate places in my ViewController
#import "Math"
- (void)viewDidLoad {
[super viewDidLoad];
Math *theMath = [Math alloc]; // makes no difference if I init[]
theMath.a = 10;
theMath.b = 20;
NSLog (#" answer is %i",theMath.c);
// but still outputs to:
// answer is 0
}
Now I know can make an iVar and do it this way,
int d = [self.theMath mathemagic:theMath.a adding:theMath.b];
NSLog (#" sum: %i",d);
But i shouldn't have to. Stanford CS193P seems to always make the Class a property of the ViewController, but then everything is again expressed as self.theMath.whatever and the Data Model is no longer encapsulated away from the VC ? Maybe Stanford leaves advanced distractions to Java graduates till later.
Well for this person who's read David Flanagan's "Java in A Nutshell" ,
and Niemeyer-Knudsen's "Learning Java", It's later Now.
I shouldn't have to touch theMath.c, just by assigning values to [ theMath.a ] and [ theMath.b ] should be enough.
Where am I wrong?
I think that is because you are setting a and b = 0 in alloc init . and you are not calling [self mathemagic:a adding:b] anywhere.
I think im Math.m you should change -(instancetype)init to
- (instancetype)initWith:(int)a andb:(int)b {
self = [super init];
if (self) {
_c = [self mathemagic:a adding:b];
}
return self;
}
and in viewDidLoad use
Math *theMath = [[Math alloc]initWith:10 andb:20];
Hope this helps :)
I think you have a misconception of how Objective-C classes work.
First of all, it takes two steps to create an object in Objective-C. You must both:
Dynamically allocate memory for the new object
Initialize the newly allocated memory to appropriate values
So your Math instance initialization should look like this:
Math *theMath = [[Math alloc] init];
Just calling alloc zeroes out all instance variables of the object. Although in your case it makes no difference using [Math alloc] or [[Math alloc] init], it's not good programming style.
Second, if by "automatically display behaviour" you mean logging the result of mathemagic:adding: method, then you should pass it as an argument to NSLog function instead of theMath.c
NSLog(#" should show the sum being %i", [theMath mathemagic:theMath.a adding:theMath.b]);

Objective-C Address of property expression

I need access address of property but have problem. example code is
#interface Rectangle : NSObject
{
SDL_Rect wall;
SDL_Rect ground;
}
#property SDL_Rect wall;
#property SDL_Rect ground;
#end
#implementation Rectangle
#synthesize x;
#synthesize y;
#end
#interface Graphics : NSObject
{
int w;
int h;
}
-(void) drawSurface
#end
#implementation Graphics
-(void) drawSurface
{
Rectangle *rect = [[Rectangle alloc] init];
SDL_BlitSurface(camera, NULL, background, &rect.wall);
}
#end
&rect.x is Address of property expression requested
As the comments suggest, you cannot take the address of a property. A property is really just a promise that the object in question provides accessors for some value. The value itself may or may not even exist in an instance variable. For example, the getter for a property called fullName might generate the required value on the fly by concatenating the values of firstName and lastName properties.
Since you need to pass the address of a SDL_Rect into SDL_BlitSurface(), you could first copy the necessary property into a local variable, and then pass the address of that variable:
Rectangle *rect = [[Rectangle alloc] init];
SDL_Rect wall = rect.wall;
SDL_BlitSurface(camera, NULL, background, &wall);
If you need to preserve the value left in wall after the call to SDL_BlitSurface(), copy it back again after the call:
rect.wall = wall;
I had a similar situation with subclasses needing to access a CGAffineTransform defined in the parent class. The answer came from #orpheist's answer to this question: Get the address of an Objective-c property (which is a C struct). It does involve adding a method to your Rectangle class.
#interface Rectangle : NSObject
{
NSRect wall;
NSRect ground;
}
#property NSRect wall;
#property NSRect ground;
#end
#implementation Rectangle
#synthesize wall = _wall; //x;
#synthesize ground = _ground; //y;
- (const NSRect *) addressOfWall {
return &_wall;
}
- (const NSRect *) addressOfGround {
return &_ground;
}
+(instancetype)standardRectangle
{
Rectangle *newInstance = [[self alloc] init];
newInstance.wall = NSMakeRect(0,0, 300, 100);
newInstance.ground = NSMakeRect(0 ,0, 300, 450);
return newInstance;
}
#end
Now you can use, for instance, addressOfWall thus:
- (void)testWall
{
Rectangle *rect = [Rectangle standardRectangle];
XCTAssertEqual(100, [rect addressOfWall]->size.height);
}
Address of property expression requested that means:
#preperty (nonatomic,copy) NSString *name;
if you want to get the address of self.name. You cannot write the code like this:
NSLog (#"%p",&(self.name));
Because in fact,self.name is getter method, like this:
- (NSString *)name {
return _name;
}
so you cannot get address of method.

Passing int between 3 classes in cocos2d

I have 3 classes namely GameScene, HomeScene and RecordsScene and I am trying to pass an integer value from GameScene to the RecordScene. My problem is, once the game is done, it goes home, then you have to click on Records to see the Records. Basically, the order of the three classes would be something like this:
GameScene --> HomeScene --> RecordScene
What I've been trying to do was pass the integer value from GameScene to the HomeScene then from the HomeScene, I'll pass it to the RecordScene but when I get there, the value of the integer is still 0. Here's my current code:
//GameScene
-(void)goHome:(ccTime)dt
{
HomeScene *home = [HomeScene node];
//myInt is an integer I declared on the HomeScene class, the value changes depending on the stage
home.layer.myInt = 1;
[[CCDirector sharedDirector]replaceScene:home];}
//HomeScene
-(void)viewRecord:(id)sender
{
//View record
RecordScene *record = [RecordScene node];
//lastStage is the integer from RecordScene I'm passing the value of myInt into
record.layer.lastStage = myInt;
[[CCDirector sharedDirector]replaceScene:record];}
I'm implementing the classes like so:
#implementation HomeScene
#synthesize layer;
-(id)init
{
if((self =[super init]))
{
self.layer = [HomeSceneLayer node];
[self addChild:layer];
}
return self;
}
#end
#implementation HomeSceneLayer
#synthesize myInt;
//methods
#end
Then my header looks something like so:
#interface HomeScene : CCLayer
{
int myInt;}
#property(nonatomic)int myInt;
#end
#interface HomeScene : CCScene
{
HomeSceneLayer *layer;
}
#property (nonatomic, retain)HomeSceneLayer *layer;
#end
Why is it resetting back to 0 and how do I pass the integer value from the GameScene to the RecordScene?
#synthesize myInt; creates an iVar _myInt.
RecordScene *record = [RecordScene node];
//lastStage is the integer from RecordScene I'm passing the value of myInt into
record.layer.lastStage = myInt;
you are setting record.layer.lastStage to myInt (iVar of HomeScene). Try record.layer.lastStage = self.layer.myInt;
From Apple's coding guidelines at https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CodingGuidelines/Articles/NamingIvarsAndTypes.html
Make sure the name of the instance variable concisely describes the attribute stored. Usually, you should not access instance variables directly, instead you should use accessor methods (you do access instance variables directly in init and dealloc methods). To help to signal this, prefix instance variable names with an underscore (_), for example:
Xcode now (I don't know since when) also synthesizes properties automatically, naming the iVar for #property [...] foo _foo.

CGPoint property used in class from other class with Lvalue error

I have two major classes in objective C using cocos2D, DebugZoneLayer and HeroClass. Using Cocos2D may not be part of the issue.
HeroClass contains a CGPoint and a property. I have an instance of HeroClass in DebugZoneLayer initialized like hero = [[HeroClass alloc] init];
My HeroClass.h shortened to show you how I create a CGPoint vel.
#interface HeroClass : CCLayer {
#public CGPoint _vel;
}
#property(assign) CGPoint vel;
In HeroClass.m I synthesize my property like #synthesize vel = _vel;
In DebugZoneLayer.m, I can reference my hero.vel x or y just fine, but anything that assigns a value to hero.vel x or y returns the error: Lvalue required as left operand of assignment
That's right — you can't do that. A property is just a method call, and methods in Objective-C always return by value, meaning the CGPoint that gets returned is just a temporary CGPoint with the same value as the one in your object. Setting the components of this temporary value isn't allowed. You'll need to either create special setters on your class for the point's X and Y values or set the whole point at a time.
Restating Chuck's entirely correct answer in a different way..
Your problem is that CGPoints are not Objective-c Objects, they are C Structs. Your property *_vel* is not an instance of an Object, like an NSArray, NSArray or DebugZoneLayer.
As a simple and lazy example, using an int instead of a struct and a bit of psuedocode..
#interface HeroClass : CCLayer {
int _numberOfLives;
}
#end
#implementation HeroClass
- (id)init {
[super init];
_numberOfLives = 3;
}
- (int)livesRemaining {
return _numberOfLives;
}
#end
you couldn't set the value of _numberOfLives like this..
foo = [[HeroClass alloc] init];
bar = [foo livesRemaining];
bar = 2;
Changing the value of bar won't change the value of foo's _numberOfLives instance variable because when you called -livesRemaining, bar was set to a copy of the current value of _numberOfLives.
In short, you need to learn you some C.

Setting nested objects?

I am just trying to make sure I am getting things right as I move forward with Objective-C, two quick questions if I may:
(1) Am I accessing the Position object correctly from within Rectangle? am I right to access the Position object contained within by the pointer I set in the init, or is there a better way?
(2) In [setPosX: andPosY:] Which of the two ways of setting the Position instance variables is best, or does it really not matter?
// INTERFACE
#interface Position: NSObject {
int posX;
int posY;
}
#property(assign) int posX;
#property(assign) int posY;
#end
#interface Rectangle : NSObject {
Position *coord;
}
-(void) setPosX:(int) inPosX andPosY:(int) inPosY;
// IMPLEMENTATION
#implementation Rectangle
-(id) init {
self = [super init];
if (self) {
NSLog(#"_init: %#", self);
coord = [[Position alloc] init];
// Released in dealloc (not shown)
}
return(self);
}
-(void) setPosX:(int) inPosX andPosY:(int) inPosY {
//[coord setPosX:inPosX];
//[coord setPosY:inPosY];
coord.posX = inPosX;
coord.posY = inPosY;
}
EDIT_01
Do I then call -(id)initWithX:andY: from the Rectangle object when I init it? and if so how do I go about setting posX and posY from within main()? or do I replace the init for rectangle with a further -(id)initWithX:andY: and pass the values through?
#implementation Rectangle
-(id) init {
self = [super init];
if (self) {
NSLog(#"_init: %#", self);
coord = [[Position alloc] initWithX:1234 andY:5678];
}
return(self);
}
...
cheers gary
(1) You're accessing it correctly.
(2) In objective-c 2.0, the assignments have identical affect.
Design wise, you would want to make:
-(void) setPosX:(int) inPosX andPosY:(int) inPosY;
...into a method of Position. This encapsulates both the data and the methods related to into one object. So you could have calls like:
coord = [[Position alloc] initWithX:inPosX andY:inPosY];
or
[coord setPosX:inPosX andPosY:inPosY];
all much cleaner and easier to maintain.
Edit O1
Do I then call -(id)initWithX:andY:
from the Rectangle object when I init
it?
That depends on your design. If the coord property is absolutely vital to the Rectangle instance, then you should call it when you initialize a Rectangle instance. You might even write an initializers for Rectangle that takes a position or x and y as input. eg:
-(id) initWithPosition:(Position *) aPos {
self = [super init];
if (self) {
NSLog(#"_init: %#", self);
coord = aPos;
// Released in dealloc (not shown)
}
return self;
}
You should also write a connivence initializer for the Position class:
-(id) initWithX:(NSInteger) x andY:(NSInteger) y{
self=[super init];
self.posX=x;
self.posY=y;
return self;
}
You would then call like:
Position *aPos=[[Position alloc] initWithX:100 andY:50];
Rectangle *aRec=[[Rectangle alloc] initWithPosition:aPos];
Or you could write another combination initializer for Rectangle:
-(id) initWithXCoordinate:(NSInteger) x andYCoordinate:(NSInteger) y{
self=[super init];
Position *aPos=[[Position alloc] initWithX:x andY:y];
self.coord=aPos;
return self;
}
and call it like:
Rectangle *aRec=[[Rectangle alloc] initWithXCoordinate:100
andYCoordinate:50];
These are rough examples but you get the idea. Objective-c gives you a lot of flexibility in setting up initializer so you can create any initializers you find convenient.
You do generally want to avoid using actual functions instead of methods inside classes.
(1) You also need to release it in -dealloc. Does it really make sense to create a Rectangle with an uninitialised position though? In other words, what's the behaviour of [[Position alloc] init], and is it reasonable that a Rectangle should ever have a Position in that state?
(2) They both do the same thing. The one you've written is clearer than the one you've commented out, because it indicates that you're changing properties rather than getting the object to do something. That's my opinion, anyway. Some people agree, others don't, and as I say the behaviour is the same.