Objective-C中的只读属性?

我已经在我的界面中声明了只读属性:

@property (readonly, nonatomic, copy) NSString* eventDomain; 

也许我误解了属性,但是我认为当你声明它是readonly ,你可以在实现( .m )文件中使用生成的setter,但是外部实体不能改变它的值。 这个SO问题说,这是应该发生的。 那是我以后的行为。 然而,当试图使用标准的setter或dot语法来设置我的init方法内的eventDomain ,它给了我一个unrecognized selector sent to instance. 错误。 当然,我在@ @synthesize财产。 试图像这样使用它:

  // inside one of my init methods [self setEventDomain:@"someString"]; // unrecognized selector sent to instance error 

那么我是否误解了对财产的readonly声明呢? 或者还有其他的事情呢?

你需要告诉编译器你也需要一个setter。 一个常见的方法是把它放在.m文件中的类扩展中:

 @interface YourClass () @property (nonatomic, copy) NSString* eventDomain; @end 

我发现使用只读属性的另一种方法是使用@synthesize指定后备存储。 例如

 @interface MyClass @property (readonly) int whatever; @end 

然后在执行

 @implementation MyClass @synthesize whatever = _whatever; @end 

你的方法可以设置_whatever,因为它是一个成员variables。


我在过去几天中意识到的另一个有趣的事情是,你可以创build只读的子类,如:

(在头文件中)

 @interface MyClass { @protected int _propertyBackingStore; } @property (readonly) int myProperty; @end 

然后,在执行

 @synthesize myProperty = _propertyBackingStore; 

它将使用头文件中的声明,所以子类可以更新属性的值,同时保持它的只读性。

在数据隐藏和封装方面略有遗憾。

Eiko和其他人给出了正确的答案。

这是一个更简单的方法: 直接访问私有成员variables。

在头文件.h文件中:

@property (strong, nonatomic, readonly) NSString* foo;

在执行.m文件中:

 // inside one of my init methods self->_foo = @"someString"; // Notice the underscore prefix of var name. 

就是这样,这就是你所需要的。 没有麻烦,没有大惊小怪。

细节

从Xcode 4.4和LLVM Compiler 4.0( Xcode 4.4中的新特性 )开始,您不需要讨论其他答案中讨论的杂项:

  • synthesize关键字
  • 声明一个variables
  • 在实现.m文件中重新声明该属性。

在声明一个属性foo ,你可以假设Xcode已经添加了一个以下划线的前缀命名的私有成员variables: _foo

如果该属性被声明为readwrite ,则Xcode将生成一个名为foo的getter方法和一个名为setFoo的setter。 当您使用点符号(我的Object.myMethod)时,这些方法隐式调用。 如果该属性声明为readonly ,则不会生成setter。 这意味着用下划线命名的支持variables本身不是只读的。 readonly意味着没有合成器方法被合成,因此使用点符号来设置一个值会失败并出现编译器错误。 点符号失败,因为编译器阻止你调用一个不存在的方法(setter)。

最简单的方法是直接访问用下划线命名的成员variables。 即使没有声明下划线命名的variables,也可以这样做! Xcode插入声明作为构build/编译过程的一部分,所以你的编译代码将确实有variables声明。 但是你永远不会看到你的原始源代码文件中的声明。 不是魔术,只是语法糖 。

使用self->是访问对象/实例的成员variables的一种方式。 您可能可以省略,只使用var名称。 但我更喜欢使用自我+箭头,因为它使我的代码自我logging。 当你看到self->_foo你已经知道了_foo是这个实例的成员variables。


顺便说一下,讨论财产访问者与直接伊娃访问的正反两面,正是你在马特·纽伯格博士编程的iOS书中读到的一种深思熟虑的治疗。 我发现阅读和重读非常有帮助。

请参阅在iOS文档中自定义现有类 。

只读指示该属性是只读的。 如果指定只读,则@implementation中只需要一个getter方法。 如果在实现块中使用@synthesize,则只有getter方法被合成。 此外,如果您尝试使用点语法分配值,则会出现编译器错误。

只读属性只有一个getter方法。 您仍然可以直接在属性的类中或使用键值编码设置后援伊娃。

你误解了另一个问题。 在这个问题中有一个类扩展,如下所示:

 @interface MYShapeEditorDocument () @property (readwrite, copy) NSArray *shapesInOrderBackToFront; @end 

这就是在类的实现中只能看到setter的东西。 正如Eiko所说,你需要声明一个类扩展,并覆盖属性声明来告诉编译器只在类中产生一个setter。

最短的解决scheme是:

MyClass.h

 @interface MyClass { int myProperty; } @property (readonly) int myProperty; @end 

MyClass.h

 @implementation MyClass @synthesize myProperty; @end 

如果一个属性被定义为只读的,那么就意味着它不会是一个可以在类内部使用或从其他类的外部使用的setter。 (即:如果有意义的话,你将只有一个“getter”。)

从它的声音,你想要一个标准的读/写属性标记为私人,你可以通过在你的接口文件中将类variables设置为private来实现:

 @private NSString* eventDomain; }