我已在我的界面中声明了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
声明?或者还有其他事情发生了吗?
答案 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)
这是一种更简单的方法:直接访问私有成员变量。
示例强>
在标题.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
关键字声明属性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;
}