Objective-C中的只读属性?

时间:2011-01-03 17:05:41

标签: objective-c properties readonly

我已在我的界面中声明了readonly属性:

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

也许我误解了属性,但我认为当你将其声明为readonly时,你可以在实现(.m)文件中使用生成的setter,但是外部实体不能改变值。 This SO question说这是应该发生的事情。这就是我追求的行为。但是,当尝试使用标准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声明?或者还有其他事情发生了吗?

7 个答案:

答案 0 :(得分:109)

您需要告诉编译器您还需要一个setter。一种常见的方法是将其放在.m文件中的class extension中:

@interface YourClass ()

@property (nonatomic, copy) NSString* eventDomain;

@end

答案 1 :(得分:35)

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

@interface MyClass

@property (readonly) int whatever;

@end

然后在实施中

@implementation MyClass

@synthesize whatever = _whatever;

@end

然后你的方法可以设置_whatever,因为它是一个成员变量。


我在过去几天中意识到的另一个有趣的事情是,您可以创建可由子类写入的只读属性:

(在头文件中)

@interface MyClass
{
    @protected
    int _propertyBackingStore;
}

@property (readonly) int myProperty;

@end

然后,在实现中

@synthesize myProperty = _propertyBackingStore;

它将在头文件中使用声明,因此子类可以更新属性的值,同时保留其readonlyness。

但在数据隐藏和封装方面有点令人遗憾。

答案 2 :(得分:33)

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

这是一种更简单的方法:直接访问私有成员变量。

示例

在标题.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(New Features in Xcode 4.4)开始,你不必搞乱其他答案中讨论的琐事:

  • synthesize关键字
  • 声明变量
  • 在实现.m文件中重新声明属性。

声明属性foo后,您可以假设Xcode添加了一个名为前缀为下划线的私有成员变量:_foo

如果声明了属性readwrite,Xcode会生成一个名为foo的getter方法和一个名为setFoo的setter。使用点表示法(我的Object.myMethod)时会隐式调用这些方法。如果属性声明为readonly,则不会生成任何setter。这意味着以下划线命名的支持变量 not 本身只读。 readonly意味着没有合成任何setter方法,因此使用点符号设置值会因编译器错误而失败。点表示法失败,因为编译器阻止您调用不存在的方法(setter)。

最简单的方法是直接访问使用下划线命名的成员变量。即使没有声明下划线命名变量,您也可以这样做! Xcode将该声明作为构建/编译过程的一部分插入,因此您的编译代码确实具有变量声明。但是您从未在原始源代码文件中看到该声明。不是魔术,只是syntactic sugar

使用self->是一种访问对象/实例的成员变量的方法。您可以省略它,只使用var名称。但我更喜欢使用self + arrow,因为它使我的代码自我记录。当您看到self->_foo时,您不知道_foo是此实例中的成员变量。


顺便说一句,讨论财产访问者与直接ivar访问的利弊,正是你在Matt Neuberg Programming iOS博士书中所读到的那种深思熟虑的处理方式。我发现阅读和重读很有帮助。

答案 3 :(得分:20)

请参阅iOS文档中的Customizing Existing Classes

  

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

只读属性只有getter方法。您仍然可以直接在属性的类中或使用键值编码设置支持ivar。

答案 4 :(得分:7)

你误解了另一个问题。 In that question有一个类扩展,声明如下:

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

这就是生成只在类的实现中可见的setter的原因。因此,正如Eiko所说,你需要声明一个类扩展并覆盖属性声明,告诉编译器只在类中生成一个setter。

答案 5 :(得分:4)

最短的解决方案是:

MyClass.h

@interface MyClass {

  int myProperty;

}

@property (readonly) int myProperty;

@end

MyClass.h

@implementation MyClass

@synthesize myProperty;

@end

答案 6 :(得分:2)

如果属性被定义为只读,则意味着实际上不存在可以在类内部使用或在其他类外部使用的setter。 (即:如果有意义的话,你只会有一个“吸气剂”。)

从它的声音中,您需要一个标记为私有的正常读/写属性,您可以通过在接口文件中将类变量设置为私有来实现:

@private
    NSString* eventDomain;
}