我想测试一个对象在iPhone SDK中是否有可写的@property。
这样做的一种可行方法是检查-valueForKey:方法,但这看起来相当不优雅!
示例:
@try {
id *value = [instance valueForKey:@"myProperty"];
}
@catch (NSException * e) {
// Key did not exist
}
有更好的方法吗?
答案 0 :(得分:54)
如果您要创建正在检查的对象,则可以覆盖valueForUndefinedKey:
和setValue:forUndefinedKey
以执行比引发异常更有用的操作。
另一方面,如果您试图在运行时内省您不了解的对象,则必须使用运行时方法来执行此操作。您可以使用objective-c运行时本身并调用class_copyPropertyList
或protocol_copyPropertyList
并处理这些内容,或使用Foundation并在对象上调用respondsToSelector
以获取KVC getter / setter给定属性,例如,对于属性foo
,您可以调用类似[someObject respondsToSelector:NSSelectorFromString(@"foo")];
的内容。
答案 1 :(得分:5)
通常,无法确定对象是否具有给定键的值。实例可能决定从其-valueForUndefinedKey:
方法返回其他未定义键的值。或者它可能让默认实现抛出异常。现代Objective-C(2.0)对象经常在其公共API中声明相关属性。对于这些对象,您可以使用Objective-C运行时的class_copyPropertyList
或protocol_copyPropertyList
来获取可用属性的列表。您还可以模拟KVC搜索列表,如果实例响应适当的getter方法,则通过repondsToSelector:
进行测试。最后,您可以使用运行时的class_copyIvarList
来检查ivars是否有合适的ivar。这是不应该做的很多工作。它不能保证您在发送valueForKey:
消息时知道对象实例是否会引发异常并且它表示更大的问题......
如果您有一组对象必须全部为给定键提供值,但有些对象没有,那么您就遇到了设计问题。您应该定义一个适当的接口(Objective-C中的@protocol
)来声明所需的属性。然后,您可以测试是否符合此协议,或者使用编译器为您执行某些编译时类型检查,以确保实例符合协议(如果您传递单个实例)。
答案 2 :(得分:0)
继续@Jason Coco回答。
以下是关于如何专门检查您尝试设置的属性的“设置”选择器的提供。
这是纯粹的污秽,但它有效,我发现自己正在使用它。你已被警告过..
NS_INLINE SEL _Nonnull IBPropertySetSelectorForPropertyName(NSString*_Nonnull propertyName)
{
NSString* firstLetter = [propertyName substringToIndex:1];
propertyName = [propertyName substringFromIndex:1];
propertyName = [[NSString alloc] initWithFormat:@"set%@%@:",firstLetter.capitalizedString,propertyName];
return NSSelectorFromString(propertyName);
}
<强>用法:强>
在swift中: 将代码段添加到桥接标题中:
let key = "someKey"
let sel = IBPropertySetSelectorForPropertyName(key)
if SOMETHING.responds(to: sel) {SOMETHING.setValue(value, forKeyPath: key)}
答案 3 :(得分:0)
您在问题中提出的try / catch方法是了解valueForKey:
是否会引发异常的唯一可靠方法。
@try {
id *value = [instance valueForKey:@"myProperty"];
}
@catch (NSException * e) {
// Key did not exist
}