Objective-C中的只读属性?

Ale*_*lex 89 properties objective-c readonly

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

 @property (readonly, nonatomic, copy) NSString* eventDomain;
Run Code Online (Sandbox Code Playgroud)

也许我误解了属性,但我认为当你声明它时readonly,你可以在implementation(.m)文件中使用生成的setter ,但是外部实体不能改变它的值.这个问题说的是应该发生的事情.这就是我追求的行为.但是,当尝试使用标准setter或dot语法设置eventDomain我的init方法时,它会给我一个unrecognized selector sent to instance.错误.我当然是@synthesize属于该物业.试着像这样使用它:

 // inside one of my init methods
 [self setEventDomain:@"someString"]; // unrecognized selector sent to instance error
Run Code Online (Sandbox Code Playgroud)

我是否误解readonly了财产上的声明?或者还有其他事情发生了吗?

Eik*_*iko 111

您需要告诉编译器您还需要一个setter.一种常见的方法是将它放在.m文件中的类扩展中:

@interface YourClass ()

@property (nonatomic, copy) NSString* eventDomain;

@end
Run Code Online (Sandbox Code Playgroud)

  • 它不是一个类别.这是一个类扩展(如Eiko所说).虽然用于类似目的,但它们明显不同.例如,您不能在真正的类别中执行上述操作. (22认同)
  • 这不是很公平的bbum.它可能不同,但它被称为"匿名类别" (5认同)
  • @Madbreaks它是附加的,但它在.m文件中.所以编译器知道让它为该类进行读写,但外部使用仍然只能按预期进行读取. (4认同)
  • 这是除了op在公共接口中的初始声明吗?意思是,这个类扩展声明是否覆盖了`.h`中的初始声明?否则我不知道这会如何暴露公共二传手.谢谢 (3认同)
  • 我注意到一个继承自具有类扩展属性的类的类将不会看到它们.即继承只看到外部接口.确认? (2认同)

yan*_*ano 36

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

@interface MyClass

@property (readonly) int whatever;

@end
Run Code Online (Sandbox Code Playgroud)

然后在实施中

@implementation MyClass

@synthesize whatever = m_whatever;

@end
Run Code Online (Sandbox Code Playgroud)

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


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

(在头文件中)

@interface MyClass
{
    @protected
    int m_propertyBackingStore;
}

@property (readonly) int myProperty;

@end
Run Code Online (Sandbox Code Playgroud)

然后,在实施中

@synthesize myProperty = m_propertyBackingStore;
Run Code Online (Sandbox Code Playgroud)

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

稍微遗憾的是,在数据隐藏和封装方面.


Bas*_*que 35

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.
Run Code Online (Sandbox Code Playgroud)

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

细节

从Xcode 4.4和LLVM Compiler 4.0(Xcode 4.4中的新功能)开始,您不必搞乱其他答案中讨论的杂事:

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

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

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

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

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


顺便说一下,讨论物业访问者与直接ivar访问的利弊,正是Matt Neuberg博士的iOS编程书中你会想到的那种深思熟虑的处理方式.我发现阅读和重读非常有帮助.

  • @HAS正确,这里的场景是*privately*设置从该类外部看不到的成员变量.这就是原始海报问题的全部要点:将属性声明为`readonly`,这样就没有*其他*类可以设置它. (2认同)

Jon*_*nah 20

请参阅iOS文档中的自定义现有类.

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

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


Bol*_*ock 7

你误解了另一个问题.在那个问题中有一个类扩展,声明如下:

@interface MYShapeEditorDocument ()
@property (readwrite, copy) NSArray *shapesInOrderBackToFront;
@end
Run Code Online (Sandbox Code Playgroud)

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


use*_*978 5

最短的解决方案是:

MyClass.h

@interface MyClass {

  int myProperty;

}

@property (readonly) int myProperty;

@end
Run Code Online (Sandbox Code Playgroud)

MyClass.h

@implementation MyClass

@synthesize myProperty;

@end
Run Code Online (Sandbox Code Playgroud)