我正在尝试获取未知类的所有属性列表以及每个属性的类。到目前为止,我得到一个对象的所有属性的列表(我递归地获取所有的超类)。我受到this post
的启发+ (NSArray *)classPropsFor:(Class)klass
{
NSLog(@"Properties for class:%@", klass);
if (klass == NULL || klass == [NSObject class]) {
return nil;
}
NSMutableArray *results = [[NSMutableArray alloc] init];
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList(klass, &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
const char *propName = property_getName(property);
if(propName) {
NSString *propertyName = [NSString stringWithUTF8String:propName];
[results addObject:propertyName];
}
NSArray* dict = [self classPropsFor:[klass superclass]];
[results addObjectsFromArray:dict];
}
free(properties);
return [NSArray arrayWithArray:results];
}
所以现在我想要每个属性的类,我这样做:
NSArray* properties = [PropertyUtil classPropsFor:[self class]];
for (NSString* property in properties) {
id value= [self valueForKey:property];
NSLog(@"Value class for key: %@ is %@", property, [value class]);
}
问题是它适用于NSStrings,但不适用于自定义类,因为它返回null。我想递归地创建一个字典来表示一个对象,里面可以有其他对象,因为我认为我需要知道每个属性的类,这可能吗?
答案 0 :(得分:7)
为此做了一个小方法。
// Simple as.
Class propertyClass = [customObject classOfPropertyNamed:propertyName];
可以通过多种方式进行优化,但我喜欢它。
实施如下:
-(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;
}
它是eppz!kit的一部分,位于名为NSObject+EPPZRepresentable.h
的开发对象代表中。它实际上完成了你最初要实现的目标。
// Works vica-versa.
NSDictionary *representation = [customObject dictionaryRepresentation];
CustomClass = [CustomClass representableWithDictionaryRepresentation:representation];
它编码了许多类型,迭代通过集合,表示CoreGraphics类型,UIColors,也表示/重建对象引用。
新版本甚至会将 C类型名称和命名的结构类型反映回来:
NSLog(@"%@", [self typeOfPropertyNamed:@"index"]); // unsigned int
NSLog(@"%@", [self typeOfPropertyNamed:@"area"]); // CGRect
NSLog(@"%@", [self typeOfPropertyNamed:@"keyColor"]); // UIColor
eppz!model的一部分,随意在https://github.com/eppz/eppz.model/blob/master/eppz!model/NSObject%2BEPPZModel_inspecting.m#L111
使用方法实现答案 1 :(得分:2)
<强>已更新强>
这不适用于nil
的值。相反,您应该使用运行时C API从相应的ivar或accessor方法获取类。
答案 2 :(得分:2)
您应该在存储propertyName
的同时为每个属性存储类(作为字符串)。也许是一个字典,其属性名称为键,类名称为值,反之亦然。
要获取类名,您可以执行以下操作(在声明propertyName
后立即执行此操作):
NSString* propertyAttributes = [NSString stringWithUTF8String:property_getAttributes(property)];
NSArray* splitPropertyAttributes = [propertyAttributes componentsSeparatedByString:@"\""];
if ([splitPropertyAttributes count] >= 2)
{
NSLog(@"Class of property: %@", [splitPropertyAttributes objectAtIndex:1]);
}
字符串处理代码是因为属性包含许多信息 - 具体细节在此处指定:https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html
答案 3 :(得分:0)
以下添加到NSObject类别可以解决问题。
- (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;
}