测试只读属性与设置/获取键- object -c / cocoa

时间:2022-09-12 08:48:27

If all i have is a list of keys, is there an elegant way to test an object for read-only vs. read/write properties? I realize I could string-bang the key:

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

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

Or better yet try to set a value for the key and check for an NSUndefinedKeyException - but using exceptions for non-exceptional behavior seems like poor form.

或者更好的方法是为键设置一个值并检查NSUndefinedKeyException—但是对非异常行为使用异常似乎不是很好。

To be clear, i'd like to programmatically audit an object's properties and tell the difference between, for instance,

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

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

edit: To be more clear, it shouldn't matter if the keys are implemented as @propertys or manual getter/setter. Only concerned about public interface. And thanks for asking what I'm trying to accomplish - that's probably more important to get right in the first place.

编辑:更明确地说,键是否实现为@propertys或手动getter/setter都不重要。只关心公共接口。谢谢你问我想要完成什么——首先,这可能更重要。

I'm trying to sketch out some code that generates graphic representations of an object's keys. All the keys are known beforehand - but I won't always know which keys will be sett-able (it's up to the particular subclass to implement)

我正在尝试草拟一些代码来生成对象键的图形表示。所有的键都是预先知道的——但我并不总是知道哪些键是可设置的(具体实现取决于特定的子类)

4 个解决方案

#1


5  

Assuming you're fine with asking the metaclass (ie, you don't want to allow for potential instance-specific patches to the dispatch table), you can get property attributes from the runtime. Specifically:

假设您可以询问元类(例如,您不希望为分派表提供潜在的特定于实例的补丁),那么您可以从运行时获得属性属性。具体地说:

// 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);

You can then check those attributes for read-only:

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

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

return [attributes containsObject:@"R"];

If you want to be completely thorough, you should also check protocols via class_copyProtocolList and protocol_getProperty, in order to catch any @propertys that are incorporated into the class that way and — as noted below by Gabriele — some caveats apply around class extensions.

如果您想要完全彻底,您还应该通过class_copyProtocolList和protocol_getProperty检查协议,以便捕获任何以这种方式合并到类中的@propertys,并且(如下面Gabriele所述)一些注意事项适用于类扩展。

#2


7  

You have two valid approaches:

你有两种有效的方法:

  • the respondsToSelector: method
  • respondstoselectorismemberofclass:方法
  • the runtime "trick" as exposed in Tommy's answer
  • 运行时“诡计”在汤米的回答中暴露出来

I'll try to summarize the implication of both approaches, along with their drawbacks. You can then choose the approach more suitable to your needs.

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

Case 1

//MyClass.h

@property (readonly) NSString *someReadOnlyKey;
  • runtime => readonly
  • 运行时= >只读的
  • respondsToSelector => NO
  • respondstoselectorismemberofclass = >没有

Ok, everything works as expected.

好的,一切都按预期进行。

Case 2

//MyClass.h

@property (readonly) NSString *someReadOnlyKey;

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

//MyClass.m

@property (readwrite) NSString *someReadOnlyKey;
  • runtime => readwrite
  • 运行时= >读写
  • respondsToSelector => YES
  • respondstoselectorismemberofclass = >是的

If the property definition has been overridden, you'll get the actual property attribute being used. It doesn't matter whether you query about it from where the setter is visible, i.e. inside the class definition, or from outside. You will get the actual definition that has been used to synthesize the accessor methods.

如果属性定义被重写,您将获得正在使用的实际属性属性。无论您是从setter可见的地方查询它,也就是类定义内部查询,还是从外部查询。您将得到用于合成访问器方法的实际定义。

Case 3

//MyClass.h

@property (setter=myCoolSetter) NSString *someReadOnlyKey;
  • runtime => readwrite
  • 运行时= >读写
  • respondsToSelector => NO
  • respondstoselectorismemberofclass = >没有

With a custom setter name, the respondsToSelector trick won't work anymore, whereas the runtime is still providing the correct information.

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

Case 4

//MyClass.h

@property (readonly) NSString *someReadOnlyKey;

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

//MyClass.m

- (void)setSomeReadOnlyKey:(NSString *)someReadOnlyKey {
    _someReadOnlyKey = someReadOnlyKey;
}
  • runtime => readonly
  • 运行时= >只读的
  • respondsToSelector => YES
  • respondstoselectorismemberofclass = >是的

This time the runtime is failing, since the setter is there, but the property definition "doesn't know" about it.

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

#3


2  

This might be one of the few times I might consider the exception route - that is of course if you need handle KVC.

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

The reason being that just checking for a set<key>: style method name is not really enough to determine if something is really settable, especially when you bring KVC into it.

原因是仅仅检查set : style方法名并不能真正确定某些东西是否可以设置,尤其是当您将KVC引入其中时。

KVC has well defined search patterns for when you try to set values, which can be seen in the Key Value Coding Programming Guide. This search path has many stops and even allows objects a last chance "are you sure you don't want to handle this setter" method with setValue:forUndefinedKey:, which can be used to prevent the NSUndefinedKeyException exception being thrown

KVC有定义良好的搜索模式,可以在关键值编码编程指南中看到。这个搜索路径有很多停止,甚至允许对象有最后的机会“你确定你不想处理这个setter”,方法是setValue:forUndefinedKey:,它可以用来防止NSUndefinedKeyException被抛出

#4


-1  

You can use the following:

你可使用以下资料:

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

This should work. Essentially, I am checking if the setter method exists.

这应该工作。本质上,我在检查setter方法是否存在。

#1


5  

Assuming you're fine with asking the metaclass (ie, you don't want to allow for potential instance-specific patches to the dispatch table), you can get property attributes from the runtime. Specifically:

假设您可以询问元类(例如,您不希望为分派表提供潜在的特定于实例的补丁),那么您可以从运行时获得属性属性。具体地说:

// 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);

You can then check those attributes for read-only:

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

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

return [attributes containsObject:@"R"];

If you want to be completely thorough, you should also check protocols via class_copyProtocolList and protocol_getProperty, in order to catch any @propertys that are incorporated into the class that way and — as noted below by Gabriele — some caveats apply around class extensions.

如果您想要完全彻底,您还应该通过class_copyProtocolList和protocol_getProperty检查协议,以便捕获任何以这种方式合并到类中的@propertys,并且(如下面Gabriele所述)一些注意事项适用于类扩展。

#2


7  

You have two valid approaches:

你有两种有效的方法:

  • the respondsToSelector: method
  • respondstoselectorismemberofclass:方法
  • the runtime "trick" as exposed in Tommy's answer
  • 运行时“诡计”在汤米的回答中暴露出来

I'll try to summarize the implication of both approaches, along with their drawbacks. You can then choose the approach more suitable to your needs.

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

Case 1

//MyClass.h

@property (readonly) NSString *someReadOnlyKey;
  • runtime => readonly
  • 运行时= >只读的
  • respondsToSelector => NO
  • respondstoselectorismemberofclass = >没有

Ok, everything works as expected.

好的,一切都按预期进行。

Case 2

//MyClass.h

@property (readonly) NSString *someReadOnlyKey;

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

//MyClass.m

@property (readwrite) NSString *someReadOnlyKey;
  • runtime => readwrite
  • 运行时= >读写
  • respondsToSelector => YES
  • respondstoselectorismemberofclass = >是的

If the property definition has been overridden, you'll get the actual property attribute being used. It doesn't matter whether you query about it from where the setter is visible, i.e. inside the class definition, or from outside. You will get the actual definition that has been used to synthesize the accessor methods.

如果属性定义被重写,您将获得正在使用的实际属性属性。无论您是从setter可见的地方查询它,也就是类定义内部查询,还是从外部查询。您将得到用于合成访问器方法的实际定义。

Case 3

//MyClass.h

@property (setter=myCoolSetter) NSString *someReadOnlyKey;
  • runtime => readwrite
  • 运行时= >读写
  • respondsToSelector => NO
  • respondstoselectorismemberofclass = >没有

With a custom setter name, the respondsToSelector trick won't work anymore, whereas the runtime is still providing the correct information.

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

Case 4

//MyClass.h

@property (readonly) NSString *someReadOnlyKey;

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

//MyClass.m

- (void)setSomeReadOnlyKey:(NSString *)someReadOnlyKey {
    _someReadOnlyKey = someReadOnlyKey;
}
  • runtime => readonly
  • 运行时= >只读的
  • respondsToSelector => YES
  • respondstoselectorismemberofclass = >是的

This time the runtime is failing, since the setter is there, but the property definition "doesn't know" about it.

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

#3


2  

This might be one of the few times I might consider the exception route - that is of course if you need handle KVC.

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

The reason being that just checking for a set<key>: style method name is not really enough to determine if something is really settable, especially when you bring KVC into it.

原因是仅仅检查set : style方法名并不能真正确定某些东西是否可以设置,尤其是当您将KVC引入其中时。

KVC has well defined search patterns for when you try to set values, which can be seen in the Key Value Coding Programming Guide. This search path has many stops and even allows objects a last chance "are you sure you don't want to handle this setter" method with setValue:forUndefinedKey:, which can be used to prevent the NSUndefinedKeyException exception being thrown

KVC有定义良好的搜索模式,可以在关键值编码编程指南中看到。这个搜索路径有很多停止,甚至允许对象有最后的机会“你确定你不想处理这个setter”,方法是setValue:forUndefinedKey:,它可以用来防止NSUndefinedKeyException被抛出

#4


-1  

You can use the following:

你可使用以下资料:

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

This should work. Essentially, I am checking if the setter method exists.

这应该工作。本质上,我在检查setter方法是否存在。