I\'d like to test whether an object has a writeable @property in the iPhone SDK.
One possible way of doing this is to check the -valueForKey: method, but that seems rat
Following on from @Jason Coco answer.
Here is my offering on how to check specifically for the "set" selectors existence of the property you're trying to set.
This is pure filth but it works and I've found myself using it. You've been warned..
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);
}
Usage:
In swift: Add the code snippet to your bridging header then:
let key = "someKey"
let sel = IBPropertySetSelectorForPropertyName(key)
if SOMETHING.responds(to: sel) {SOMETHING.setValue(value, forKeyPath: key)}
If you are creating the object that is being checked, you could override valueForUndefinedKey:
and setValue:forUndefinedKey
to do something more useful than raising an exception.
If, on the other hand, you are trying to introspect objects you don't know about at runtime, you will have to use the runtime methods to do that. You can either use the objective-c runtime itself and call either class_copyPropertyList
or protocol_copyPropertyList
and deal with those, or use Foundation and call respondsToSelector
on the object for the KVC getter/setters for a given property, e.g., for a property foo
you would call something like [someObject respondsToSelector:NSSelectorFromString(@"foo")];
.
There is no way, in general, to determine if an object has a value for a given key. An instance may decide to return a value for an otherwise undefined key from its -valueForUndefinedKey:
method. Or it may let the default implementation throw an exception. Modern Objective-C (2.0) objects often declare relevant properties in their public API. For these objects, you can use the Objective-C runtime's class_copyPropertyList
or protocol_copyPropertyList
to get a list of the available properties. You can also emulate the KVC search list, testing via repondsToSelector:
if the instance responds to an appropriate getter method. Finally you could use the runtime's class_copyIvarList
to check the ivars for an appropriate ivar. This is a lot of work that you shouldn't be doing. It won't guarantee that you know whether an object instance will raise an exception when sent a valueForKey:
message and it indicates a bigger issue...
If you have a collection of objects that must all provide a value for a given key, but some don't, you have a design problem. You should define an appropriate interface (a @protocol
in Objective-C) that declares the needed properties. You can then test for conformance to this protocol, or make use of the compiler to do some compile-time type checking for you to make sure instance conform to the protocol (if you're passing around single instances).
The try/catch approach you propose in your question is the only reliable way of knowing whether valueForKey:
will throw an exception.
@try {
id *value = [instance valueForKey:@"myProperty"];
}
@catch (NSException * e) {
// Key did not exist
}