Reputation: 63
I Have read the already posted questions extensively and can't quite find the answer I'm looking for.
I fully understand the concept of using the @syntesize
directive to create getter and setter methods (i.e. if i had @property int width
and @synthesize width
, I am inadvertently creating a getter method of width
and a setter method of setWidth:
).
However, when I am not using the @synthesize
directive but am declaring instance variables in the @implementation
section that are objects, I do not fully understand how the accessor methods work. This is what I do not understand about the following code:
1) in main
where it says:
NSLog(@"Origin at (%i, %i)", myRect.origin1.x, myRect.origin1.y);
It appears to me as if it would be calling the [[myRect origin1] x]
method which would first determine that [myRect origin1]
returns origin
and would then immediately call [origin x]
as a result (and then do the same for y
). Now, what throws me off is the fact that if I were to change the name of the getter method
-(XYpoint *) origin1;
contained within Rectangle.h to
-(XYpoint *) origin2;
the program gets tons of errors and ceases to compile. Note: I also changed the name of this method everywhere it is referenced including changing the preceding code in main to
NSLog(@"Origin at (%i, %i)", myRect.origin2.x, myRect.origin2.y);
However if I also change the name of the setter method from:
-(void) setOrigin1: (XYpoint *) pt
to:
-(void) setOrigin2: (XYpoint *) pt
then everything works as it did before. It seems to me it only works correctly when my getter and setter are both named in the x
setX
naming convention. I supposed this is mainly what I need explained:
A) If I create an instance variable that happens to be an object (like 'origin' in this case) must I create getter and setter methods for it?
B) Can I create a getter method but not a setter method or vice versa
C) Is it mandatory that if I do create both a getter and setter method for 'origin' that they both be named in the x
setX
manner. In this case as -(XYpoint *) origin1
and -(void) setOrigin1: (XYpoint *) pt
. As in if I change the name of the getter I must change the name of the setter accordingly?
Here is all of the code:
Rectangle.h:
#import <Foundation/Foundation.h>
@class XYpoint;
@interface Rectangle : NSObject
@property int width, height;
-(XYpoint *) origin1;
-(void) setOrigin1: (XYpoint *) pt;
-(void) setWidth: (int) w andHeight: (int) h;
-(int) area;
-(int) perimeter;
@end
Rectangle.m:
#import "Rectangle.h"
@implementation Rectangle
{
XYpoint *origin;
}
@synthesize width, height;
-(void) setWidth:(int) w andHeight:(int)h
{
width = w;
height = h;
}
-(void) setOrigin1: (XYpoint *) pt
{
origin = pt;
}
-(int) area
{
return width * height;
}
-(int) perimeter
{
return (width + height) * 2;
}
-(XYpoint *) origin1
{
return origin;
}
@end
XYpoint.h:
#import <Foundation/Foundation.h>
@interface XYpoint : NSObject
@property int x, y;
-(void) setX: (int) xVal andY: (int) yVal;
@end
XYpoint.m:
#import "XYpoint.h"
@implementation XYpoint
@synthesize x,y;
-(void) setX: (int) xVal andY: (int) yVal
{
x = xVal;
y = yVal;
}
@end
main.m:
#import <Foundation/Foundation.h>
#import "Rectangle.h"
#import "XYpoint.h"
int main (int argc, const char * argv[])
{
@autoreleasepool {
Rectangle *myRect = [[Rectangle alloc] init];
XYpoint *myPoint = [[XYpoint alloc] init];
[myPoint setX: 100 andY: 200];
[myRect setWidth: 5 andHeight:8];
myRect.origin1 = myPoint;
NSLog(@"Rectangle w = %i, h = %i", myRect.width, myRect.height);
NSLog(@"Origin at (%i, %i)", myRect.origin1.x, myRect.origin1.y);
NSLog(@"Area = %i, Perimeter = %i", [myRect area], [myRect perimeter]);
}
return 0;
}
Upvotes: 6
Views: 3250
Reputation: 12041
After discussion via email, we found out that the issue actually seems to be a bug in clang. Consider the following mini program:
#import <Foundation/Foundation.h>
@interface TestObject : NSObject
-(void)setIdVar:(id)someId;
@end
@implementation TestObject
-(void)setIdVar:(id)someId;
{
NSLog(@"-setIdVar called with argument: %@", someId);
}
@end
int main (int argc, const char * argv[])
{
@autoreleasepool {
TestObject *testObj = [[TestObject alloc] init];
testObj.idVar = @"test";
}
return 0;
}
Obviously, we expect this program to run and output -setIdVar called with argument: test
. And that's exactly what happens when you compile it without ARC (e.g.. using clang -framework Foundation main.m
).
But if we compile it with ARC, clang crashes. (clang -framework Foundation -fobjc-arc main.m
)
Funny thing is, this crash doesn't happen when using setters for non-object types (eg. int) or when a getter is defined.
Upvotes: 3
Reputation: 12041
You most likely forgot to change the method names in the header or implementation files. It's perfectly valid to have read-only properties (without setter methods).
The best practice if you have an object property that you want to access using the dot-notation (ie. myRect.origin1
), is to make sure you define the corresponding property in the header file, ie. include a line such as:
@property(readonly) XYPoint *origin1; // for read only properties
@property(retain) XYPoint *origin1; // for read/write properties
Use them even if you don't use @synthesize
, and use them instead of the normal method declarations in the header file. These lines don't actually create getters and setters, they just inform the compiler that your class has these properties. The compiler will then expect getters (and setters if you don't use readonly) named -origin1
and -setOrigin1
. The names of the setters/getters are important (see Apple's Documentation on Key-Value Coding for details)
You should also be aware of Cocoa's memory management guidelines: Unless you are using Automatic reference counting, your Rectangle class is responsible for retaining or copying the XYPoint object in the setter. [EDIT]: I just realised that you are obviously using ARC since you use the @autoreleasepool
syntax.
Upvotes: 3
Reputation: 125017
A) If I create an instance variable that happens to be an object (like 'origin' in this case) must I create getter and setter methods for it?
No. If you declare a property, you'll need to either provide your own accessors or use the @synthesize directive to create them. But you can have all the instance variables you like without having accessors for them.
B) Can I create a getter method but not a setter method or vice versa
Yes, you can provide just the getter if you declare your property readonly
.
C) Is it mandatory that if I do create both a getter and setter method for 'origin' that they both be named in the x setX manner. In this case as -(XYpoint *) origin1 and -(void) setOrigin1: (XYpoint *) pt. As in if I change the name of the getter I must change the name of the setter accordingly?
You can provide your own names for the accessors, but you should stick with the usual convention if you want your class to be key value coding compliant for the property in question:
@property (getter=isBar, setter=setBar) int bar;
Upvotes: 5