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

时间:2011-12-20 17:57:56

标签: objective-c

我已经广泛阅读了已经发布的问题,并且找不到我正在寻找的答案。

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

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

1)在@implementation中说:

main

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

y

包含在Rectangle.h中

-(XYpoint *) origin1;

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

-(XYpoint *) origin2;

但是,如果我也改变了setter方法的名称:

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

为:

-(void) setOrigin1: (XYpoint *) pt

然后一切都像以前一样有效。在我看来,只有当我的getter和setter都在-(void) setOrigin2: (XYpoint *) pt x命名约定中命名时,它才能正常工作。我认为这主要是我需要解释的内容:

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

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

C)如果我确实为'origin'创建了getter和setter方法,它们都必须以setX x方式命名。在本例中为setX-(XYpoint *) origin1。如果我更改了getter的名称,我必须相应地更改setter的名称吗?

以下是所有代码:

Rectangle.h:

-(void) setOrigin1: (XYpoint *) pt

Rectangle.m:

#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

XYpoint.h:

#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.m:

#import <Foundation/Foundation.h>

@interface XYpoint : NSObject

@property int x, y;

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

的main.m:

#import "XYpoint.h"

@implementation XYpoint

@synthesize x,y;

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

3 个答案:

答案 0 :(得分:5)

  

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

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

  

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

是的,如果您声明属性readonly,则可以仅提供获取者。

  

C)如果我确实创建了getter和setter方法,那是强制性的吗?   对于'origin',它们都以x setX方式命名。在这   case as - (XYpoint *)origin1和 - (void)setOrigin1:(XYpoint *)pt。   如果我改变了getter的名字,我必须更改名称   相应的setter?

您可以为访问者提供自己的名称,但如果您希望您的类符合相关属性的键值编码,则应遵循通常的惯例:

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

答案 1 :(得分:3)

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

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

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

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

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

答案 2 :(得分:3)

通过电子邮件讨论后,我们发现问题实际上似乎是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;
}

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

但如果我们用ARC编译它,clang会崩溃。 (clang -framework Foundation -fobjc-arc main.m

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