Objective-C/Cocoa: 如何为只读属性设置值?

3

我发现一些对象被定义为只读属性被外部对象设置,但是不知道原因。例如,在:

// skview is an instance of SKView
// skscene is an instance of SKScene
[skview presentScene:skscene]

skscene的“view”属性是只读的,它被设置为skview。

如果skview.view属性是只读的,那么这是如何实现的呢?在自定义对象中,如何实现这种行为?

谢谢!

3个回答

14
如果你在.h文件中声明了一个属性并将其设置为readonly,则可以在.m文件(延续类别中)中重新声明它为readwrite,因此可以直接编辑该值。
但这只是规范的做法。您还可以使用KVC来进行更加后门式的操作,并且可以调用setValue:forKey:,这将绕过您可能指定的任何公共内容。
查看此处,了解有关KVC如何满足您的请求的说明。
然后,如果您真的要绝望,可以使用Obj-C运行时直接访问底层实例变量。

感谢@Wain。但是在.m文件中声明属性只能在.m文件内部访问setter,而不能被外部对象访问。在我的例子中,我认为SKView对象无法看到在SKScene的.m文件中声明的属性,对吗?SKView只能看到SKScene的.h中的只读属性,对吗? - George
1
从技术上讲,一切都可以以某种方式获得。让编译器满意只是其中的一部分。如果您在另一个类上声明一个类别并添加一个方法,编译器会很高兴。重要的是在运行时起作用的内容,这几乎包括您想要的任何访问... - Wain

1
理解这一点的关键是认识“接口”和“实现”的基本区别。
@interface 定义了您特定对象想要呈现的公开可见面。对于外部世界,它可以声明“这个值是只读的,您不能更改它”。
然而,在内部,实现可以自由地将其设置为可读写,直接访问接口正在暴露的后备存储,或将只读属性实现为计算,即不表示简单变量的东西。
例如,想象一个名为'firstSubview'的属性 - 这个只读属性将通过检索所有子视图的列表来实现,然后返回数组中的第一个(如果没有,则返回nil)。使此属性可写会没有多大意义。
请记住,@implementation 可以重新定义属性为 readwrite,可以创建自己的私有属性(和后备变量)等。
把.h文件的内容看作主要是给除了实际实现.m之外的所有人使用的。

我相信你没有理解这个问题。在我所提到的例子中,SKScene的“view”只读属性是由外部的SKView对象修改的,而不是由SKScene对象本身修改的。如果你阅读SKScene的文档,你会意识到没有公共方法可以让一个SKScene对象知道它必须改变它的“view”属性,因为它已经被添加到了一个SKView中。 - George
你看不到一个访问器来设置公共API中的视图,并不意味着SceneKit内部没有提供私有类别来提供该访问。当你调用-[SKView presentScene:]时,框架很可能使用私有API来管理你所引用的信息。我重申之前的说法 - @interface只需要暴露你被允许操作的公共API。框架不需要向你公开所有内部工作。 - Jeff Laing
谢谢,但你还是没有理解我的问题:我不是在问发生了什么的解释,因为我已经知道了。我想知道的是如何实现它。 - George
在SKView内,您可以在SKScene上声明一个类别,将属性从只读提升为可读写(假设该属性实际上是作为具有后备存储的内容实现的)。我已经在许多框架类中完成了这个过程。有一个“framework-private.h”文件,被导入到所有实现中,公开了“内部API”,而没有人支持它们。框架内的类共享秘密知识,但这没关系。一旦您理解了@interface可以分布在许多文件中,包括硬编码在.m文件中,这确实非常简单。 - Jeff Laing
当然,你不能仅仅“声明属性为可读写”,并希望所有的内部工具都已经准备就绪-你的框架源代码必须以协同的方式编写。但是,你的内部头文件可以公开一个比公共头文件更丰富的API。在这里要注意的另一件事情,当然是你不知道“view”是否真的是“简单属性”。从外部很难区分一个从后端存储返回值的属性和一个返回计算结果的属性。 - Jeff Laing

0
如果您想让外部项目设置此属性,那么您需要声明一个setter。例如,在您的.h文件中,您会有以下内容:
@property (readonly) NSString *mySpecialString;

-(void)setMySpecialString:(NSString*)string;

然后您需要在.m文件中编写实现,并且您还可以在其中放置任何希望在赋值时发生的其他操作。但是,在这种情况下,没有必要使此属性只读,因为您既可以读取又可以写入它。
使用只读属性的一种方法是使它们可供外部类读取,然后在.m文件中修改相应的ivars,以便将它们私下写入。

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