3

如果我只有一个键列表,是否有一种优雅的方法来测试对象的只读与读/写属性?我意识到我可以按弦键:

NSString *setterString = [@"set" stringByAppendingString:[someKeyString capitalizedString]];
BOOL isReadWrite = [myObject respondsToSelector:NSSelectorFromString(setterString)];

或者更好的是尝试为键设置一个值并检查一个NSUndefinedKeyException- 但对非异常行为使用异常似乎是一种糟糕的形式。

为了清楚起见,我想以编程方式审核对象的属性并区分例如,

@property (readonly) NSString *someReadOnlyKey
@property NSString *someReadWriteProperty

编辑:为了清楚,键是作为@propertys 实现还是手动 getter/setter 并不重要。只关心公共接口。并感谢您询问我要完成的工作-首先要做到这一点可能更重要。

我正在尝试绘制一些生成对象键的图形表示的代码。所有的键都是事先知道的——但我并不总是知道哪些键是可以设置的(这取决于要实现的特定子类)

4

4 回答 4

7

您有两种有效的方法:

  • respondsToSelector:方法_
  • 汤米的回答中公开的运行时“技巧”

我将尝试总结这两种方法的含义以及它们的缺点。然后,您可以选择更适合您需求的方法。

情况1

//MyClass.h

@property (readonly) NSString *someReadOnlyKey;
  • 运行时 =>只读
  • 响应选择器 =>

好的,一切都按预期工作。

案例2

//MyClass.h

@property (readonly) NSString *someReadOnlyKey;

///////////////////////////////////////////////////////////////////////////

//MyClass.m

@property (readwrite) NSString *someReadOnlyKey;
  • 运行时 =>读写
  • 响应选择器 =>

如果属性定义已被覆盖,您将获得正在使用的实际属性属性。无论您是从 setter 可见的位置(即在类定义内部还是从外部)查询它都没有关系。您将获得用于合成访问器方法的实际定义。

案例3

//MyClass.h

@property (setter=myCoolSetter) NSString *someReadOnlyKey;
  • 运行时 =>读写
  • 响应选择器 =>

使用自定义设置器名称,该respondsToSelector技巧将不再起作用,而运行时仍提供正确的信息。

案例4

//MyClass.h

@property (readonly) NSString *someReadOnlyKey;

///////////////////////////////////////////////////////////////////////////

//MyClass.m

- (void)setSomeReadOnlyKey:(NSString *)someReadOnlyKey {
    _someReadOnlyKey = someReadOnlyKey;
}
  • 运行时 =>只读
  • 响应选择器 =>

这次运行时失败了,因为设置器在那里,但属性定义“不知道”它。

于 2013-08-27T01:08:32.557 回答
5

假设您可以询问元类(即,您不想为调度表提供潜在的特定于实例的补丁),您可以从runtime获取属性属性。具体来说:

// 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_copyProtocolListand检查协议protocol_getProperty,以便捕获@property以这种方式合并到类中的任何 s 并且 - 如下文 Gabriele 所述 - 一些注意事项适用于类扩展。

于 2013-08-27T00:32:52.237 回答
2

这可能是我考虑异常路径的少数几次之一——当然,如果你需要处理 KVC。

原因在于仅检查set<key>:样式方法名称并不足以确定某些内容是否真正可设置,尤其是当您将 KVC 引入其中时。

当您尝试设置值时,KVC 有明确定义的搜索模式,可以在Key Value Coding Programming Guide中看到。这个搜索路径有很多停止点,甚至允许对象最后一次机会“你确定你不想处理这个 setter”方法setValue:forUndefinedKey:,它可以用来防止NSUndefinedKeyException抛出异常

于 2013-08-27T01:15:08.857 回答
-1

您可以使用以下内容:

if ([obj respondsToSelector:@selector(setSomeReadOnlyKey:someReadOnlyKey:)]) {
   // Not a readonly property
}
else
   // Readonly property

这应该有效。本质上,我正在检查 setter 方法是否存在。

于 2013-08-27T00:34:31.840 回答