测试只读属性与设置/获取密钥——obj-c / cocoa
Posted
技术标签:
【中文标题】测试只读属性与设置/获取密钥——obj-c / cocoa【英文标题】:test for read-only property vs. set/get key -- obj-c / cocoa 【发布时间】:2013-08-27 00:10:49 【问题描述】:如果我只有一个键列表,是否有一种优雅的方法来测试对象的只读与读/写属性?我意识到我可以按弦键:
NSString *setterString = [@"set" stringByAppendingString:[someKeyString capitalizedString]];
BOOL isReadWrite = [myObject respondsToSelector:NSSelectorFromString(setterString)];
或者更好的是尝试为键设置一个值并检查NSUndefinedKeyException
- 但是对非异常行为使用异常似乎是一种糟糕的形式。
为了清楚起见,我想以编程方式审核对象的属性并区分例如,
@property (readonly) NSString *someReadOnlyKey
@property NSString *someReadWriteProperty
编辑:更清楚地说,键是实现为 @property
s 还是手动 getter/setter 并不重要。只关心公共接口。感谢您询问我想要完成的工作 - 首先要做到这一点可能更重要。
我正在尝试勾勒出一些生成对象键的图形表示的代码。所有的键都是事先知道的——但我并不总是知道哪些键是可设置的(这取决于特定的子类来实现)
【问题讨论】:
在类扩展中可以将readonly
属性声明为readwrite
,这对您的实现是否重要?
您还应该考虑到您可能有一个声明为readonly
的属性,然后去手动实现一个setter - objc 运行时恶作剧可能看起来很有趣,但很可能会给出很多假阴性,说事情是readonly
,当它们实际上是通过重新声明或手动实现 setter 进行读写时。
您真正想要实现的目标是什么?
@paul.s 对。如果您关心 ivar 是否有关联的 setter,请相信 respondsToSelector:
。如果您专注于声明属性的方式,那么询问运行时是一种处理它的优雅方式。
@Paul.s - 感谢您的警告和好问题。我已经编辑了我的问题,试图更具体地说明问题。
【参考方案1】:
您有两种有效的方法:
respondsToSelector:
方法
Tommy 的回答中公开的运行时“技巧”
我将尝试总结这两种方法的含义以及它们的缺点。然后,您可以选择更适合您需求的方法。
案例一
//MyClass.h
@property (readonly) NSString *someReadOnlyKey;
运行时 => 只读
respondsToSelector => 否
好的,一切都按预期进行。
案例 2
//MyClass.h
@property (readonly) NSString *someReadOnlyKey;
///////////////////////////////////////////////////////////////////////////
//MyClass.m
@property (readwrite) NSString *someReadOnlyKey;
运行时 => 读写
respondsToSelector => 是
如果属性定义已被覆盖,您将获得正在使用的 actual 属性属性。无论您是从 setter 可见的位置(即在类定义内部还是从外部)查询它无关紧要。您将获得用于合成访问器方法的实际定义。
案例 3
//MyClass.h
@property (setter=myCoolSetter) NSString *someReadOnlyKey;
运行时 => 读写
respondsToSelector => 否
使用自定义 setter 名称,respondsToSelector
技巧将不再起作用,而运行时仍在提供正确的信息。
案例 4
//MyClass.h
@property (readonly) NSString *someReadOnlyKey;
///////////////////////////////////////////////////////////////////////////
//MyClass.m
- (void)setSomeReadOnlyKey:(NSString *)someReadOnlyKey
_someReadOnlyKey = someReadOnlyKey;
运行时 => 只读
respondsToSelector => 是
这次运行时失败了,因为 setter 在那里,但属性定义“不知道”它。
【讨论】:
【参考方案2】:假设您可以询问元类(即,您不想为调度表提供潜在的特定于实例的补丁),您可以从the runtime 获得property attributes。具体来说:
// get the property; yes: that's a C string. This can see only things
// declared as @property
objc_property_t property =
class_getProperty([instance class], "propertyName");
/* check property for NULL here */
// get the property attributes.
const char *propertyAttributes = property_getAttributes(property);
然后您可以检查这些属性是否为只读:
NSArray *attributes =
[[NSString stringWithUTF8String:propertyAttributes]
componentsSeparatedByString:@","];
return [attributes containsObject:@"R"];
如果您想彻底彻底,您还应该通过class_copyProtocolList
和protocol_getProperty
检查协议,以便捕获以这种方式合并到类中的任何@property
s,并且——正如Gabriele 下面所指出的——一些注意事项适用于类扩展。
【讨论】:
为什么这比使用 respondsToSelector 更好? 从技术上讲,您可以为 ivar 提供一个 setter 实现,并且仍然将属性声明为readonly
。这在很大程度上取决于 OP 的需求。
除了区分语义上不同的“实现了什么设置器”和“什么@propertys 被声明为读/写”,这将捕获任何一个设置器被赋予不寻常名称的实例。
@Tommy 同意,但它可能会错过属性在外部声明为 readonly
并在类扩展中重新定义为 readwrite
的情况。答案是正确的,但问题并不完全清楚。
调度表的实例特定补丁?这是现在可能的事情吗?最后我检查了一下,更改对象方法列表的唯一方法是更改其类。这有改变吗?【参考方案3】:
这可能是我考虑异常路径的少数几次之一 - 当然,如果您需要处理 KVC。
原因在于仅检查 set<key>:
样式的方法名称并不足以确定某些内容是否真正可设置,尤其是当您将 KVC 引入其中时。
当您尝试设置值时,KVC 具有明确定义的搜索模式,这可以在 Key Value Coding Programming Guide 中看到。这个搜索路径有很多停止点,甚至允许对象最后一次机会“你确定你不想处理这个 setter”方法与setValue:forUndefinedKey:
,可以用来防止NSUndefinedKeyException
异常被抛出
【讨论】:
【参考方案4】:您可以使用以下内容:
if ([obj respondsToSelector:@selector(setSomeReadOnlyKey:someReadOnlyKey:)])
// Not a readonly property
else
// Readonly property
这应该可行。本质上,我正在检查 setter 方法是否存在。
【讨论】:
以上是关于测试只读属性与设置/获取密钥——obj-c / cocoa的主要内容,如果未能解决你的问题,请参考以下文章