I\'m trying to get a list of all the properties of an unknown class and the class of every property. By the moment I get a list of all the properties of an object(I do it recurs
You should probably store the class (as a string) for each property at the same time as you store the propertyName
. Maybe as a dictionary with property name as the key and class name as the value, or vice versa.
To get the class name, you can do something like this (put this right after you declare propertyName
):
NSString* propertyAttributes = [NSString stringWithUTF8String:property_getAttributes(property)];
NSArray* splitPropertyAttributes = [propertyAttributes componentsSeparatedByString:@"\""];
if ([splitPropertyAttributes count] >= 2)
{
NSLog(@"Class of property: %@", [splitPropertyAttributes objectAtIndex:1]);
}
The string handling code is because the attributes include a number of pieces of information - the exact details are specified here: https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html
UPDATED
This doesn't work for values that are nil
. Instead you should use the runtime C API to obtain the class from the corresponding ivar or accessor method.
Just made a tiny method for this.
// Simple as.
Class propertyClass = [customObject classOfPropertyNamed:propertyName];
Could be optimized in many ways, but I love it.
Implementation goes like:
-(Class)classOfPropertyNamed:(NSString*) propertyName
{
// Get Class of property to be populated.
Class propertyClass = nil;
objc_property_t property = class_getProperty([self class], [propertyName UTF8String]);
NSString *propertyAttributes = [NSString stringWithCString:property_getAttributes(property) encoding:NSUTF8StringEncoding];
NSArray *splitPropertyAttributes = [propertyAttributes componentsSeparatedByString:@","];
if (splitPropertyAttributes.count > 0)
{
// xcdoc://ios//library/prerelease/ios/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html
NSString *encodeType = splitPropertyAttributes[0];
NSArray *splitEncodeType = [encodeType componentsSeparatedByString:@"\""];
NSString *className = splitEncodeType[1];
propertyClass = NSClassFromString(className);
}
return propertyClass;
}
It is part of eppz!kit, within a developing object representer called NSObject+EPPZRepresentable.h. It actually does what you are to achieve originally.
// Works vica-versa.
NSDictionary *representation = [customObject dictionaryRepresentation];
CustomClass = [CustomClass representableWithDictionaryRepresentation:representation];
It encodes many types, iterate trough collections, represents CoreGraphics types, UIColors, also represent / reconstruct object references.
New version spits you back even C type names and named struct types as well:
NSLog(@"%@", [self typeOfPropertyNamed:@"index"]); // unsigned int
NSLog(@"%@", [self typeOfPropertyNamed:@"area"]); // CGRect
NSLog(@"%@", [self typeOfPropertyNamed:@"keyColor"]); // UIColor
Part of eppz!model, feel free to use method implementations at https://github.com/eppz/eppz.model/blob/master/eppz!model/NSObject%2BEPPZModel_inspecting.m#L111
The following added to an NSObject category does the trick.
- (Class) classForKeyPath:(NSString*)keyPath {
Class class = 0;
unsigned int n = 0;
objc_property_t* properties = class_copyPropertyList(self.class, &n);
for (unsigned int i=0; i<n; i++) {
objc_property_t* property = properties + i;
NSString* name = [NSString stringWithCString:property_getName(*property) encoding:NSUTF8StringEncoding];
if (![keyPath isEqualToString:name]) continue;
const char* attributes = property_getAttributes(*property);
if (attributes[1] == '@') {
NSMutableString* className = [NSMutableString new];
for (int j=3; attributes[j] && attributes[j]!='"'; j++)
[className appendFormat:@"%c", attributes[j]];
class = NSClassFromString(className);
}
break;
}
free(properties);
return class;
}