测试只读属性与set/get键 - obj-c/cocoa

Sam*_*ett 3 cocoa objective-c key-value-coding

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

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

或者更好的是尝试为密钥设置一个值并检查NSUndefinedKeyException- 但是对非异常行为使用异常似乎是不好的形式.

为了清楚起见,我想以编程方式审计对象的属性,并告诉它们之间的区别,例如,

@property (readonly) NSString *someReadOnlyKey
@property NSString *someReadWriteProperty
Run Code Online (Sandbox Code Playgroud)

编辑:为了清楚,如果将键实现为@propertys或手动getter/setter ,则无关紧要.只关注公共接口.并且感谢您询问我想要实现的目标 - 首先要做到这一点可能更为重要.

我试图勾勒出一些生成对象键的图形表示的代码.所有的密钥都是事先知道的 - 但我不会总是知道哪些密钥可以建立(这取决于特定的子类实现)

Gab*_*lla 7

您有两种有效的方法:

  • respondsToSelector:方法
  • Tommy的答案中暴露出的运行时"技巧"

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

情况1

//MyClass.h

@property (readonly) NSString *someReadOnlyKey;
Run Code Online (Sandbox Code Playgroud)
  • runtime => readonly
  • respondsToSelector =>

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

案例2

//MyClass.h

@property (readonly) NSString *someReadOnlyKey;

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

//MyClass.m

@property (readwrite) NSString *someReadOnlyKey;
Run Code Online (Sandbox Code Playgroud)
  • runtime => readwrite
  • respondsToSelector =>

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

案例3

//MyClass.h

@property (setter=myCoolSetter) NSString *someReadOnlyKey;
Run Code Online (Sandbox Code Playgroud)
  • runtime => readwrite
  • respondsToSelector =>

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

案例4

//MyClass.h

@property (readonly) NSString *someReadOnlyKey;

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

//MyClass.m

- (void)setSomeReadOnlyKey:(NSString *)someReadOnlyKey {
    _someReadOnlyKey = someReadOnlyKey;
}
Run Code Online (Sandbox Code Playgroud)
  • runtime => readonly
  • respondsToSelector =>

这次运行时失败,因为setter在那里,但属性定义"不知道"它.


Tom*_*mmy 5

假设您可以询问元类(即,您不希望允许针对调度表的潜在特定于实例的修补程序),您可以从运行时获取属性属性.特别:

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

然后,您可以检查这些属性是否为只读:

NSArray *attributes = 
    [[NSString stringWithUTF8String:propertyAttributes] 
        componentsSeparatedByString:@","];

return [attributes containsObject:@"R"];
Run Code Online (Sandbox Code Playgroud)

如果你想要完全彻底,你也应该通过class_copyProtocolList和检查协议protocol_getProperty,以便捕获任何@property以这种方式被纳入课程的内容,并且 - 如下面由Gabriele所述 - 一些警告适用于类扩展.