views:

98

answers:

2

I am new Objective C. I am trying to understand and workout problems from Stephen Kochan's Programming in objective c 2.0. This problem is from chapter 8.

What I don't understand is: why can't I assign variable values directly when I can get them.

For example:

This does not give me a error or warning. But it also does not set the value of X and Y.

go.origin.x = 300;
go.origin.y = 100;

But, this works fine and gives me the intended result:

NSLog (@"print %i, %i", go.origin.x, go.origin.y);

Here is my XYPoint.h

#import <Foundation/Foundation.h>

@interface XYPoint : NSObject {

    int _x;
    int _y;

}
@property int x;
@property int y;

@end

XYPoint.m

#import "XYPoint.h"

@implementation XYPoint

@synthesize x = _x;
@synthesize y = _y;
@end

GraphicObject.h

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

@interface GraphicObject : NSObject {

    XYPoint *_origin;
}

@property (nonatomic, retain) XYPoint *origin;

-(void) setOrigin : (XYPoint *) pt;

@end

GraphicObject.m

#import "GraphicObject.h"

@implementation GraphicObject

@synthesize origin = _origin;

-(void) setOrigin : (XYPoint *) pt {

    _origin = [[XYPoint alloc] init];

    _origin = pt;

    //_origin.x = pt.x;
    //_origin.y = pt.y;

}

@end

Also if don't want my origin.x and origin.y to be changed when I change my xypoint x and y, why do I have to do this:

_origin.x = pt.x;
_origin.y = pt.y;

and why not just:

_origin = pt;

For example in the main file:

XYPoint *myPoint = [[XYPoint alloc] init];
go.origin = myPoint;

myPoint.x = 250;
myPoint.y = 50;

NSLog(@"Origin values %i, %i", go.origin.x, go.origin.y);

myPoint.x = 100;
myPoint.y = 10;

NSLog(@"Origin values %i, %i", go.origin.x, go.origin.y);

I understand that the values of origin don't change. But do I have to, in the setOrigin: method, assign it in this way:

_origin.x = pt.x 

and not like this:

_origin = pt;

Thanks everyone. I know this is a long question. I really tried to understand and also looked in many places but I don't even know what to search for. What do you call this kind of assignment?

A: 
@property int x;
@property int y;

...should be...

@property (nonatomic, readwrite) int x;
@property (nonatomic, readwrite) int y;
Philip Regan
This is an iPhone question. You should replace `NSRect` with `CGRect` and `NSMakeRect` with `CGRectMake`
Jasarien
Yes, you're right, I've been working too much in the desktop recently, that I forgot about the subtlety of the iPhone. But I also found the reason for the OP's error. I really shouldn't answer this stuff in a hurry.
Philip Regan
The above statement did not work. Also, Jasarien, I am new to objective c, so correct me if I am wrong but I am not using NSRect and NSMakeRect. Could you also take a look about my other question. Thanks
Dives
+2  A: 

Congrats! You've discovered that Objective-C properties are syntactic sugar around a getter method and a setter method. Because a property is not truly a field of an object (as x and y are fields of a CGPoint structure), modifying fields of the object's properties does nothing--the code is resolved to mean "get the value of the property, then modify the resultant copy of that value."

Now, you can chain property gets and sets (e.g. anObject.widget.gear.speed *= 2) and you can chain field gets and sets (e.g. myStructure.owner.uniqueID = rand()) and, as you can see, they look pretty much identical. But you can't mix and match them.

Why?

Well, because... you can't. And that's pretty much it. So, in the future, if you're having these sorts of problems, check to see if you're mixing and matching properties and fields. If so, you'll have to break it apart:

CGPoint pt = go.origin;
pt.x += 300;
pt.y -= 100;
go.origin = pt;
Jonathan Grynspan
Thanks for clearing that up. I had a hard time understanding that before your explanation. Could also clear up my second doubt about assigning _origin.x = pt.x rather than _origin = pt. Thanks again.
Dives
You can directly assign the value of a structure to another structure of the same type, so `_origin = pt` would work if they were structures. However, because you created a class that mimics the `CGPoint` structure, an assignment like that is an assignment *by reference*, not *by value*. So you're saying "make `_origin` refer to the same object as `pt`", but that leaves the original reference in `go` unmodified.
Jonathan Grynspan
Yes it does work but as in the example above when I change myPoint.x or myPoint.y, the values of _origin.x and _origin.y also change. But when I declare _origin.x = pt.x and _origin.y = pt.y. The values of _origin don't change even when I change myPoint.
Dives
The following is a memory leak, as it replaces the **reference**, not the value, of _origin: ` _origin = [[XYPoint alloc] init]; _origin = pt;`. Subsequently, setting the fields of `_origin` modifies the original `pt`, and `_origin.ANYTHING = pt.ANYTHING` becomes a no-op.Because you are using a similar name to an existing Apple type which is *not* an object, your code is a bit confusing to read. Is there a reason why you're not using `CGPoint`?
Jonathan Grynspan
The reason for not using CGPoint is that I am going through the book: Programming in objective C 2.0 and I haven't reached the point where I can use CGPoint(I only have a vague idea what CGPoint is). This is in chapter 8 and I think CGPoint is covered after chapter 15 or so. And speaking of memory leak, I do release it by declaring dealloc in GraphicObject.m file. But I think I understand your explanation and I will keep this in mind when I reach the pertaining chapter. Thanks again for taking the time to answer my questions.
Dives
**Declaring `-dealloc` does nothing on its own.** It tells Cocoa what to do when it's time to destroy your object, but it does *not* make the object automatically destroyed when no longer in use. **To prevent a memory leak in iOS, you *must* release an object (by sending the `-release` message to it) when you no longer need it.**
Jonathan Grynspan