iOS获取属性类

6

我正在尝试获取一个未知类的所有属性及每个属性所属的类。目前,我已经获得了对象的所有属性列表(我进行递归操作以获取所有超类)。我受到这篇文章的启发。

+ (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]);
}

问题是它可以用于NSString但不能用于自定义类,因为它会返回null。我想递归地创建一个表示可以包含其他对象的对象的字典,并且我需要知道每个属性的类,这是否可能?

4个回答

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


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


2

更新

这种方法对于值为nil的情况不起作用。相反,你应该使用运行时C API从相应的实例变量或访问器方法中获取类。


如果属性为nil,这将如何生成类? - jlehr
谢谢!我们会注意空值的情况 :) - Jpellat
不客气。另一个需要考虑的是调用访问器方法可能会产生副作用,例如延迟初始化,这可能不是您想要在此时触发的。 - jlehr
我最喜欢这个答案,最坏的情况下,您可以使用KVC并将值卸载到“id fooBar”,然后只需请求“fooBar”的类。如果返回“nil”,则知道您没有正确的键路径。 - Dean Kelly

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;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接