在Objective-C中编写只读属性

31

我遇到了一些关于Objective-C属性的问题。我需要为一个变量分配一个只写属性,与readonly完全相反,也就是说,该变量可以有setMethod,但不应该有getMethod。我不知道如何实现它。带有一些代码片段的答案会更好。


2
写入属性有什么用?在我看来,这只是浪费内存。 - Eimantas
21
很多情况下,当你希望让某个内容对外只读而在类内部可读写时,就会使用“读写”(readwrite)属性。 - bbum
当外部类知道需要传递的变量,但没有理由知道它当前设置为什么时。 - ArtOfWarfare
3个回答

54

另一种有效的方法是声明普通属性并使getter不可用

@interface MyClass
@property NSString * var;
- (NSString *)var UNAVAILABLE_ATTRIBUTE;
@end

如果有人尝试访问这个getter,它会在编译时引发错误。


这种方法的主要优点是你实际上拥有一个真正的属性TM,而不是由实例变量和setter伪造的代理,那意味着:

  • 在使用Xcode中的点符号时,您将获得自动完成功能
  • 所有运行时函数(如class_getPropertyclass_copyPropertyList)都将按预期工作。

话虽如此,getter实现仍然是由程序自动生成的,并且可以通过类似以下方式调用:

NSString * string = [myObj performSelector:@selector(var)];

或者使用NSInvocation(对于非id属性)。

如果您十分担心并且确实想要防止客户端调用getter方法,您可以提供自己的实现来显式抛出异常。

@implementation MyClass
- (int)var {
    [NSException raise:NSInternalInconsistencyException
                format:@"property is write-only"];
}
@end

无论如何,如果有人真的想篡改您的实例变量,他们可以使用运行时来访问它,因此,最后一道防线在使用情况下可能是无用的。编译时错误是您真正寻找的。


LLVM 可用吗? - Mingming

12

你可以这样做:

@interface MyClass {
@private
int _var;
}

- (void)setVar:(int)newVar;
@end

@implementation MyClass
- (void)setVar:(int)newVar {
_var = newVar;
}
@end

现在你可以像写入属性一样访问变量 var

@implementation SomeOtherClass
...
MyClass c = [[MyClass alloc] init];
c.var = 3;
...
@end

如果您尝试读取虚假属性,编译器将会警告您。


1
需要在实现和接口中定义一个方法-(void)setVar:(int)newVar - JeremyP
4
从技术上讲,这是一个属性。在 Obj-C 中,属性只是 getter 和 setter 两种方法的高端名称。你可以声明一个 getter 而不使用 @property,行为与使用 @property 完全相同。setter 也是如此。你甚至可以使用点符号表示法。 - Sulthan
2
@Sulthan 几乎正确,除了运行时会做出这种区别。像这样的 属性,在使用 class_copyPropertyList 列出时不会被列出。请参见我的答案以了解不同的方法。 - Gabriele Petronella
1
@Sulthan 同意。我解决方案的最大优势可能是自动完成(而且更短;))。 - Gabriele Petronella
2
如果你在Swift中使用这个类,将其作为一个真正的属性会有所不同。如果它不是一个属性,你就不能在Swift中使用点符号表示法。 - ThomasW
显示剩余2条评论

5

这需要声明为属性吗?我只会将setter声明为方法,并使用正常的[foo setVar:bar]语法。


是的,那样可以,但是xCode似乎不能在您输入时自动完成变量。 - Fredrik Johansson
@FredrikJohansson 你说得对。如果想要一个自动完成友好的解决方案,你可能想看看我的解决方案。 - Gabriele Petronella

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接