Lens C - Accessors, i.e. Getters / setters

I have read the questions already posted widely and cannot 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 inadvertently create the getter width method and setter setWidth: method).

However, when I do not use the @synthesize directive, but declare instance variables in the @implementation section, which are objects, I don’t quite understand how access methods work. Here 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 seems to me that he will call the method [[myRect origin1] x] , which first determines that [myRect origin1] returns origin , and then immediately calls [origin x] as a result (and then does the same for y ). Now I'm sick of the fact that if I changed the name of the getter method

 -(XYpoint *) origin1; 

contained in Rectangle.h until

 -(XYpoint *) origin2; 

the program receives a lot of errors and stops compiling. Note. I also changed the name of this method everywhere it refers, including changing the previous 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 

in

 -(void) setOrigin2: (XYpoint *) pt 

then everything works as before. It seems to me that it only works correctly when my getter and setter are both named in the x setX naming setX . I suggested that this is basically what I need to explain:

A) If I create an instance variable that is an object (for example, "origin" in this case), should 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 if I create a getter and setter method for "origin", which both will be named in the style of x setX . In this case, both -(XYpoint *) origin1 and -(void) setOrigin1: (XYpoint *) pt . As in the case when I change the name of the recipient, should I change the name of the setter accordingly?

Here is the whole 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; } 
+6
source share
3 answers

After discussing via email, we found out that the problem 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 print -setIdVar called with argument: test . And this is exactly what happens when you compile it without ARC (for example, using clang -framework Foundation main.m ).

But if we compile it with ARC, clang crashes. ( clang -framework Foundation -fobjc-arc main.m )

It is funny that this failure does not occur when using setters for non-objects (for example, int) or when a getter is defined.

+3
source

A) If I create an instance variable that turns out to be an object (for example, 'origin' in this case) should I create getter and setter methods for this?

Not. If you declare a property, you need to either provide your own accessors or use the @synthesize directive to create them. But you can have all the instance variables that you like without having accessories for them.

B) Can I create a getter method, but not a setter method or vice versa

Yes, you can only provide the recipient if you declare your readonly property.

C) Is it mandatory if I create a getter and setter method for 'origin' so that both are named in the order x setX. In this case, both - ((XYpoint *) initial1 and - (void) setOrigin1: (XYpoint *) pt. As in the case when I change the name of the recipient, should I change the name of the setter accordingly?

You can specify your names for accessories, but you must adhere to the usual convention if you want your class to be encoded with key values ​​for the corresponding property:

 @property (getter=isBar, setter=setBar) int bar; 
+5
source

You most likely forgot to change the names of the methods in the header or implementation files. It is great for read-only properties (without setting methods).

Best practice, if you have a property of the object you want to access using dot notation (i.e. myRect.origin1 ), is to define the corresponding property in the header file, i.e. include a string, for example:

 @property(readonly) XYPoint *origin1; // for read only properties @property(retain) XYPoint *origin1; // for read/write properties 

Use them even if you are not using @synthesize , and use them instead of the usual method declarations in the header file. These lines do not actually create getters and setters, they simply inform the compiler that your class has these properties. The compiler then expects getters (and setters if you are not using readonly) with the names -origin1 and -setOrigin1 . Filter / getter names are important (see Apple Keyed Encoding documentation for details)

You should also be aware of Cocoa's memory management rules: if you do not use automatic reference counting, your Rectangle class is responsible for saving or copying the XYPoint object in the installer. [EDIT]: I just realized that you are obviously using ARC as you are using @autoreleasepool syntax.

+3
source

Source: https://habr.com/ru/post/904182/


All Articles