Reputation: 6940
I'm in process of reading Obj-C book and I'm not fully understand whats going on.
There is code of my app in main.m
function:
Rectangle *myRect = [[Rectangle alloc]init];
XYPoint *myPoint = [[XYPoint alloc]init];
[myPoint setX:100 andY:200];
myRect.origin = myPoint;
XYPoint *theOrigin = [myRect origin];
[theOrigin setX:200];
[theOrigin setY:300];
NSLog(@"Test %i and %i", theOrigin.x, theOrigin.y);
NSLog(@"Origin at (%i, %i)", myRect.origin.x, myRect.origin.y);
You see, i insert following code:
XYPoint *theOrigin = [myRect origin];
[theOrigin setX:200];
[theOrigin setY:300];
I made it for purpose, actually, i didn't want that values to be changed. In Rectangle.m file i modified setter method as follow:
-(void)setOrigin:(XYPoint *)pt{
if (!origin){
origin = [[XYPoint alloc]init];
origin.x = pt.x;
origin.y = pt.y;
}
}
So, logic is, when origin is not zero, it can't be changed (if i understand correct). But NSLog still output values of 200 and 300, therefore, it changed after first declaration?
When try to modify my XYPoint class like this:
[myPoint setX:50 andY:50];
NSLog(@"Origin at (%i, %i)", myRect.origin.x, myRect.origin.y);
I got same output. Its confusing.
I try to modify getter method like follow (to create a copy and return copy instead):
-(XYPoint*)origin{
if (origin){
XYPoint *copy = [[XYPoint alloc]init];
copy = origin;
NSLog(@"Copy will be returned");
return copy;
} else {
return origin;
}
}
But it still appears that problem here, and output the same, values keep changing. How to modify getter method to prevent this happening?
Why is that happen? How to prevent this? Any advice would be appreciated, thanks!
Upvotes: 0
Views: 95
Reputation: 437672
You've defined your setter for origin
property such that you cannot change it once you set it. But you've defined XYPoint
such that it's a mutable class (i.e. you can change the x
and y
values). So, when you return theOrigin
of [myRect origin]
, you are still able to change its values.
Personally, I would step back from this interface. I am not a fan of a class interface that suggests that you can set some property when you really cannot. I'm also not crazy about the fact that setOrigin
is employing something akin to copy
memory semantics, but you haven't shared a property definition that makes that intent explicit.
But going back to what I presume was the intent to not let the origin
change once it's been set, I'd rather just see you set these properties when the objects are instantiated, but then leave them as readonly
so you can be confident that they won't be mutated on you. The end result is a set of classes that are entirely intuitive.
Thus:
@interface XYPoint : NSObject
@property (nonatomic, readonly) CGFloat x;
@property (nonatomic, readonly) CGFloat y;
- (instancetype)initWithX:(CGFloat)x y:(CGFloat)y;
@end
Where
@implementation XYPoint
- (instancetype)initWithX:(CGFloat)x y:(CGFloat)y {
self = [super init];
if (self) {
_x = x;
_y = y;
}
return self;
}
@end
Likewise, I'd be inclined to make the origin
of Rectangle
to be readonly
.
@interface Rectangle : NSObject
@property (nonatomic, readonly) XYPoint *origin;
- (instancetype)initWithOrigin:(XYPoint *)origin;
@end
Where
@implementation Rectangle
- (instancetype)initWithOrigin:(XYPoint *)origin {
self = [super init];
if (self) {
_origin = origin;
}
return self;
}
@end
Then once you create your XYPoint
and Rectangle
objects, you can be confident that the properties will not mutate.
XYPoint *origin = [[XYPoint alloc] initWithX:100 y:200];
Rectangle *rect = [[Rectangle alloc] initWithOrigin:origin];
There are other approaches, but I really don't like your approach of a setter that won't set the value. You can do that, but it's unintuitive. As a developer, you expect that when you set a property, that property actually changes.
Upvotes: 1