CGPoint property used in class from other class with Lvalue error - objective-c

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.

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]);

Properties and pointers

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.

Objective-C : Need advice on setting instance variables in init method

I am using ARC.
This is my .h file
...
- (id)initWithCoordinate:(CLLocationCoordinate2D)c title:(NSString *)t;
#property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
#property (nonatomic, copy) NSString *title;
...
This is my .m file
....
#synthesize coordinate, title;
- (id)initWithCoordinate:(CLLocationCoordinate2D)c title:(NSString *)t
{
self = [super init];
if (self) {
coordinate = c;
[self setTitle:t];
}
return self;
}
....
Is setting coordinate this way, the right way to do it? Given that I declare it as readonly, it seems like it is the only way to do it. What if I just use the default (i.e. readwrite), in this case, should I use the setter method [self setCoordinate] instead?
I could set the title by doing title = t as well. Compare to using the setter method, the result is the same, but what is the difference ?
Thanks! Wish I could accept all of your answers.
You're actually supposed to set ivars directly in an initializer method all the time. This is true whether or not you have a readonly or readwrite property. The documentation here even says so.
The reasoning behind this has to do with inheritance. If someone were to subclass your class and overwrite the setters for your properties such that they bypass the ivars you created (or do some other wacky thing), then suddenly your original implementation of your initializer method now no longer does what it is written to do. In particular, your initializer could end up creating an object with a weird state due to the subclass overriding your accessors. In the pre-ARC days, you could also end up with tricky (or just straight-up broken) memory situations when this sort of thing happens. The take-away message is: you should write initializers so that they will always create an object with a known valid state.
So (assuming you're using ARC) your initializer should actually be:
- (id)initWithCoordinate:(CLLocationCoordinate2D)c title:(NSString *)t
{
self = [super init];
if (self) {
coordinate = c;
title = [t copy];
}
return self;
}
Personally, I prefer to synthesize ivars with a starting underscore to clarify when I'm using the property and when I'm accessing the ivar directly (LLVM 4.0 now does this to automatically synthesized properties as well).
#synthesize coordinate = _coordinate;
#synthesize title = _title;
- (id)initWithCoordinate:(CLLocationCoordinate2D)c title:(NSString *)t
{
self = [super init];
if (self) {
_coordinate = c;
_title = [t copy];
}
return self;
}
1: As your code is now, yes, that is the right way to do it. If you weren't using ARC (assuming you are currently), you'd also want to retain the value to assert ownership. This will be done automatically under ARC. Keep in mind that that is not the only way of doing it; you could redeclare the property as readwrite in the class extension in the implementation file. This is a common practice which allows you to have the benefits of a readwrite property while having the property still be readonly to users of the class. Ex.
//MyClass.h
#interface MyClass : NSObject
#property (nonatomic, strong, readonly) NSNumber* number;
- (void) initWithNumber:(NSNumber*)number;
#end
//MyClass.m
#interface MyClass ()
#property (nonatomic, strong, readwrite) NSNumber* number;
#end
#implementation MyClass
//this changes the instance variable backing the property to _number.
#synthesize number = _number;
- (void) initWithNumber:(NSNumber*)number{
self = [super init];
if (self) {
self.number = number;
}
return self;
}
#end
At the end of the day, I'd say it's a good habit to use setters whenever you can to keep things KVO compliant and so that you always know when values change. For instance, if you have a custom UIView with a property that is reflected in its appearance, chances are you'd want to redisplay yourself when it changes. The easiest way to do this is to implement the setter yourself and call setNeedsDisplay after setting the value. You couldn't do that if you set the instance value backing the property directly; the user of the class would have to remember to call setneedsDisplay every time they set it, manually.
2: One goes through the setter method, giving you a way to know when a value is going to be set, while one sets a value to the instance variable backing the property. The setter method will always handle memory management in the way it was told to, while it's up to you to do things such as copying values for a copy setter if you assign directly to an instance variable, so that you maintain some consistent scheme. Going through setters sometimes, and not others can lead to some nasty bugs if you don't be careful. Never going through setters makes it hard to know when values change, making it near impossible to weed out invalid values. For instance, if you had an int property you wanted to limit to values in some range and someone passed in a value under the minimum limit, you'd probably want to set the property to the lowest possible value in the range. You can't do that without the value going through the setter first.
Yes, it is fine to set it like that. If you prefer to use a property all the time you can override the property to be read/write rather than read-only in a class extension. In Foo.m:
#interface Foo ()
#property (nonatomic) CLLocationCoordinate2D coordinate;
#end
#implementation Foo {
// ...
self.coordinate = c;
}
Setting the coordinate that way is correct, and is the only way to do it if you have declared the property readonly.
Setting the title using title = t is different than setting the title using [self setTitle:t]. If you directly assign to the instance variable, you will just retain the NSString instance that was passed as argument t. But if you using the accessor method, the accessor will ask the string to copy itself (because you declared the property copy). If the string you were given as argument t is actually an NSMutableString, then you will get an immutable copy of it. If the string you were given as argument t is already an immutable string, it will just return itself when asked for a copy.
self.coordinate = c;
is essentially compiled to be the same as calling
[self setCoordinate:c];
The difference between coordinate = c and [self setCoordinate:c]; is that the first is just setting a variable directly where as the second is calling a method.
The reason to be wary is that methods could potentially have side effects depending on how the implementation is written e.g. (stupid example)
- (void)setCoordinate:(CLLocationCoordinate2D)coordinate;
{
_coordinate = coordinate;
[self doSomethingCrazy];
}

Objective C: Request for member XXX in something not a structure or union

I hit the error (stated in the subject) when trying to run the following code (snippet). The error is pointing to my 3rd and 4th lines of the code below.
id shape[3];
shape[0] = [[Circle alloc]init];
shape[0].fillColor = kRed;
shape[0].shapeBounds = bound0;
Prior to this set of code I had defined the enum and struct for ShapeColor and ShapeBoundary as below
typedef enum
{
kRed,
kBlue,
kGreen,
kPurple
}ShapeColor;
typedef struct
{
int x;
int y;
int width;
int height;
}ShapeBoundary;
Also, I have defined my interface and implementation of a "Circle" class
#interface Circle : NSObject
{
ShapeColor fillColor;
ShapeBoundary shapeBounds;
}
#property ShapeColor fillColor;
#property ShapeBoundary shapeBounds;
#end
#implementation Circle
#synthesize fillColor;
#synthesize shapeBounds;
#end
I used #property and #synthesize to define my getter and setter methods for "fillColor" and 'Shapebounds". Is there something wrong with the way I am using property and synthesize to cause the error in the subject? Or is there anything I am missing out. Any advise on this is greatly appreciated.
Thanks and Regards
Zhen Hoe
In order to use dot notation for properties, the class of the variable must be statically typed or cast. That is, your code must declare the class of the object instead of using id. If you used Circle *shape[3];, or ((Circle*)shape[0]).fillColor then your errors would go away. When you want your variable to be dynamically typed (using id), you need use the equivalent methods to get the properties:
id shape[3];
shape[0] = [[Circle alloc] init];
[shape[0] setFillColor:kRed];
[shape[0] setShapeBounds:bound0];
Also make sure you include the header for the Circle class in the file where you are doing this.

Class variable defined at #implementation rather than #interface?

I'm new to Objective-C, but I am curious about something that I haven't really seen addressed anywhere else.
Could anyone tell me what is the difference between a private variable that is declared at the #interface block versus a variable that is declared within the #implementation block outside of the class methods, i.e:
#interface Someclass : NSObject {
NSString *forExample;
}
#end
vs.
#implementation Someclass
NSString *anotherExample;
-(void)methodsAndSuch {}
#end
It seems both variables ( forExample, anotherExample ) are equally accessible throughout the class and I can't really find a difference in their behaviour. Is the second form also called an instance variable?
The latter is not defining an instance variable. Rather, it is defining a global variable in the .m file. Such a variable is not unique to or part of any object instance.
Such globals have their uses (roughly equivalent C++ static members; e.g. storing a singleton instance), but normally you would define them at the top of the file before the #implementation directive.
They're very different! The one in #implementation is a global variable not unique to each instance. Imagine there were accessors for both variables, written in the obvious way. Then the difference in behavior is shown here:
Someclass* firstObject = [[Someclass alloc] init];
Someclass* secondObject = [[Someclass alloc] init];
//forExample is an instance variable, and is unique to each instance.
[firstObject setForExample:#"One"];
[secondObject setForExample:#"Two"];
NSLog(#"%#",[firstObject forExample]); //Result: "One"
NSLog(#"%#",[secondObject forExample]); //Result: "Two"
//anotherExample is a global variable, and is NOT unique to each instance.
[firstObject setAnotherExample:#"One"];
[secondObject setAnotherExample:#"Two"];
NSLog(#"%#",[firstObject anotherExample]); //Result: "Two" (!)
NSLog(#"%#",[secondObject anotherExample]); //Result: "Two"
//Both instances return "Two" because there is only ONE variable this time.
//When secondObject set it, it replaced the value that firstObject set.
If you are looking for this sort of behavior, you might be better off using a class variable, like this:
static NSString* yetAnotherExample = nil;
Then you can use class methods to interact with the variable, and it's clearly class-specific (as opposed to instance-specific or global).
If you declare a variable inside the #implementation section, you're actually creating a global variable, visible everywhere (in every method in your application).
Member variables can only be declared in the #interface section. They are only accessible in the class itself.
The private block declared inside the #implementation block is kind of dangerous, seems to me, comparing with other OOP concept e.g. Java. Its look like member variable but kinda static.
Novice programmer can easily fooled with it. I write a test program and surprised with the behaviour.
#interface SomeClass : NSObject
{
NSString *forExample;
}
- (void) set:(NSString *)one another:(NSString *)another;
- (void)print;
#end
Implementation:
#import "SomeClass.h"
#implementation SomeClass
NSString *anotherExample;
- (void) set:(NSString *)one another:(NSString *)another
{
forExample = one;
anotherExample = another;
}
- (void)print{
NSLog(#"One = %#, another = %#", forExample, anotherExample);
}
#end
Test:
- (void)testClass {
SomeClass * s1 = [SomeClass new];
[s1 set:#"one one" another:#"one another"];
SomeClass *s2 = [SomeClass new];
[s2 set:#"two one" another:#"two another"];
[s1 print];
[s2 print];
}
And the output is,
One = one one, another = two another
One = two one, another = two another
Use a code snippet to tell the difference between a member variable and a global variable:
#implementation MyClass {
// It is an ivar, or called member variable
// Can NOT be initialized when defined.
// Can be accessed with `self->_i`
int _i;
}
- (instancetype)init {
if (self = [super init]) {
_i = 2; // should be initialized before being used.
}
return self;
}
int i = 9; // Global variable, and can be initialized when defined.
- (void)myFun {
NSLog(#"%i, %i", self->_i, i);
}
#end
// Another file
extern int i;
NSLog(#"%i", i);
Just to be clear, never ever ever declare an IBOutlet as a global var (in the implementation) if you are using it for localized nibs/xibs.
I spent a few hours figuring why the outlet is connectable only in one of the localized nibs at any given time.
Thanks for this question and the answers!