目标C - 访问者,即Getters / Setters

时间:2022-09-24 21:36:49

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:).

我完全理解使用@syntesize指令创建getter和setter方法的概念(即如果我有@property int width和@synthesize width,我无意中创建了一个宽度的getter方法和setWidth的setter方法:)。

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:

但是,当我没有使用@synthesize指令但是在@implementation部分中声明实例变量是对象时,我并不完全理解访问器方法的工作方式。这是我对以下代码不了解的内容:

1) in main where it says:

1)在主要地方它说:

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

在我看来好像它会调用[[myRect origin1] x]方法,它首先确定[myRect origin1]返回原点,然后立即调用[origin x]作为结果(然后对y执行相同操作) )。现在,让我失望的是,如果我要更改getter方法的名称

-(XYpoint *) origin1;

contained within Rectangle.h to

包含在Rectangle.h中

-(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

程序得到大量错误并停止编译。注意:我还在所引用的任何地方更改了此方法的名称,包括将main中的前面代码更改为

NSLog(@"Origin at (%i, %i)", myRect.origin2.x, myRect.origin2.y);

However if I also change the name of the setter method from:

但是,如果我还更改了setter方法的名称:

-(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:

然后一切都像以前一样有效。在我看来它只能在我的getter和setter都在x setX命名约定中命名时才能正常工作。我认为这主要是我需要解释的:

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?

A)如果我创建一个碰巧是对象的实例变量(在这种情况下就像'origin'),我必须为它创建getter和setter方法吗?

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

B)我可以创建一个getter方法但不能创建setter方法,反之亦然

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?

C)如果我确实为'origin'创建了getter和setter方法,它们都必须以x setX方式命名。在这种情况下,作为 - (XYpoint *)origin1和 - (void)setOrigin1:(XYpoint *)pt。如果我更改了getter的名称,我必须相应地更改setter的名称?

Here is all of the code:

以下是所有代码:

Rectangle.h:

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:

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:

XYpoint.h:

#import <Foundation/Foundation.h>

@interface XYpoint : NSObject

@property int x, y;

-(void) setX: (int) xVal andY: (int) yVal;
@end

XYpoint.m:

XYpoint.m:

#import "XYpoint.h"

@implementation XYpoint

@synthesize x,y;

-(void) setX: (int) xVal andY: (int) yVal
{
    x = xVal;
    y = yVal;
}
@end

main.m:

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;
}

3 个解决方案

#1


3  

After discussion via email, we found out that the issue actually seems to be a bug in clang. Consider the following mini program:

通过电子邮件讨论后,我们发现问题实际上似乎是clang中的一个错误。考虑以下迷你程序:

#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).

显然,我们希望这个程序运行并使用参数:test调用输出-setIdVar。而这恰恰是在没有ARC的情况下编译它时发生的情况(例如,使用clang -framework Foundation main.m)。

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

但是如果我们用ARC编译它,clang会崩溃。 (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.

有趣的是,当使用setter作为非对象类型(例如int)或定义了getter时,不会发生这种崩溃。

#2


5  

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?

A)如果我创建一个碰巧是对象的实例变量(在这种情况下就像'origin'),我必须为它创建getter和setter方法吗?

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.

不可以。如果您声明属性,则需要提供自己的访问者或使用@synthesize指令创建它们。但是你可以拥有你喜欢的所有实例变量而不需要访问器。

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

B)我可以创建一个getter方法但不能创建setter方法,反之亦然

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?

C)如果我确实为'origin'创建了getter和setter方法,它们都必须以x setX方式命名。在这种情况下,作为 - (XYpoint *)origin1和 - (void)setOrigin1:(XYpoint *)pt。如果我更改了getter的名称,我必须相应地更改setter的名称?

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;

#3


3  

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).

您很可能忘记更改标头或实现文件中的方法名称。拥有只读属性(没有setter方法)是完全有效的。

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:

如果您想要使用点符号(即myRect.origin1)访问的对象属性,最佳做法是确保在头文件中定义相应的属性,即。包括如下行:

@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)

即使您不使用@synthesize,也要使用它们,并使用它们而不是头文件中的常规方法声明。这些行实际上并不创建getter和setter,它们只是通知编译器您的类具有这些属性。然后,编译器将指望getter(如果不使用readonly则设置setter)名为-origin1和-setOrigin1。 setter / getter的名称很重要(有关详细信息,请参阅Apple关于键值编码的文档)

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.

您还应该了解Cocoa的内存管理准则:除非您使用自动引用计数,否则Rectangle类负责在setter中保留或复制XYPoint对象。 [编辑]:我刚刚意识到你使用@,因为你使用@autoreleasepool语法。

#1


3  

After discussion via email, we found out that the issue actually seems to be a bug in clang. Consider the following mini program:

通过电子邮件讨论后,我们发现问题实际上似乎是clang中的一个错误。考虑以下迷你程序:

#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).

显然,我们希望这个程序运行并使用参数:test调用输出-setIdVar。而这恰恰是在没有ARC的情况下编译它时发生的情况(例如,使用clang -framework Foundation main.m)。

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

但是如果我们用ARC编译它,clang会崩溃。 (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.

有趣的是,当使用setter作为非对象类型(例如int)或定义了getter时,不会发生这种崩溃。

#2


5  

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?

A)如果我创建一个碰巧是对象的实例变量(在这种情况下就像'origin'),我必须为它创建getter和setter方法吗?

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.

不可以。如果您声明属性,则需要提供自己的访问者或使用@synthesize指令创建它们。但是你可以拥有你喜欢的所有实例变量而不需要访问器。

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

B)我可以创建一个getter方法但不能创建setter方法,反之亦然

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?

C)如果我确实为'origin'创建了getter和setter方法,它们都必须以x setX方式命名。在这种情况下,作为 - (XYpoint *)origin1和 - (void)setOrigin1:(XYpoint *)pt。如果我更改了getter的名称,我必须相应地更改setter的名称?

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;

#3


3  

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).

您很可能忘记更改标头或实现文件中的方法名称。拥有只读属性(没有setter方法)是完全有效的。

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:

如果您想要使用点符号(即myRect.origin1)访问的对象属性,最佳做法是确保在头文件中定义相应的属性,即。包括如下行:

@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)

即使您不使用@synthesize,也要使用它们,并使用它们而不是头文件中的常规方法声明。这些行实际上并不创建getter和setter,它们只是通知编译器您的类具有这些属性。然后,编译器将指望getter(如果不使用readonly则设置setter)名为-origin1和-setOrigin1。 setter / getter的名称很重要(有关详细信息,请参阅Apple关于键值编码的文档)

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.

您还应该了解Cocoa的内存管理准则:除非您使用自动引用计数,否则Rectangle类负责在setter中保留或复制XYPoint对象。 [编辑]:我刚刚意识到你使用@,因为你使用@autoreleasepool语法。