Get an object properties list in Objective-C Get an object properties list in Objective-C objective-c objective-c

Get an object properties list in Objective-C


I just managed to get the answer myself. By using the Obj-C Runtime Library, I had access to the properties the way I wanted:

- (void)myMethod {    unsigned int outCount, i;    objc_property_t *properties = class_copyPropertyList([self class], &outCount);    for(i = 0; i < outCount; i++) {        objc_property_t property = properties[i];        const char *propName = property_getName(property);        if(propName) {            const char *propType = getPropertyType(property);            NSString *propertyName = [NSString stringWithCString:propName                                                                encoding:[NSString defaultCStringEncoding]];            NSString *propertyType = [NSString stringWithCString:propType                                                                encoding:[NSString defaultCStringEncoding]];            ...        }    }    free(properties);}

This required me to make a 'getPropertyType' C function, which is mainly taken from an Apple code sample (can't remember right now the exact source):

static const char *getPropertyType(objc_property_t property) {    const char *attributes = property_getAttributes(property);    char buffer[1 + strlen(attributes)];    strcpy(buffer, attributes);    char *state = buffer, *attribute;    while ((attribute = strsep(&state, ",")) != NULL) {        if (attribute[0] == 'T') {            if (strlen(attribute) <= 4) {                break;            }            return (const char *)[[NSData dataWithBytes:(attribute + 3) length:strlen(attribute) - 4] bytes];        }    }    return "@";}


@boliva's answer is good, but needs a little extra to handle primitives, like int, long, float, double, etc.

I built off of his to add this functionality.

// PropertyUtil.h#import @interface PropertyUtil : NSObject+ (NSDictionary *)classPropsFor:(Class)klass;@end// PropertyUtil.m#import "PropertyUtil.h"#import "objc/runtime.h"@implementation PropertyUtilstatic const char * getPropertyType(objc_property_t property) {    const char *attributes = property_getAttributes(property);    printf("attributes=%s\n", attributes);    char buffer[1 + strlen(attributes)];    strcpy(buffer, attributes);    char *state = buffer, *attribute;    while ((attribute = strsep(&state, ",")) != NULL) {        if (attribute[0] == 'T' && attribute[1] != '@') {            // it's a C primitive type:            /*                 if you want a list of what will be returned for these primitives, search online for                "objective-c" "Property Attribute Description Examples"                apple docs list plenty of examples of what you get for int "i", long "l", unsigned "I", struct, etc.                        */            return (const char *)[[NSData dataWithBytes:(attribute + 1) length:strlen(attribute) - 1] bytes];        }                else if (attribute[0] == 'T' && attribute[1] == '@' && strlen(attribute) == 2) {            // it's an ObjC id type:            return "id";        }        else if (attribute[0] == 'T' && attribute[1] == '@') {            // it's another ObjC object type:            return (const char *)[[NSData dataWithBytes:(attribute + 3) length:strlen(attribute) - 4] bytes];        }    }    return "";}+ (NSDictionary *)classPropsFor:(Class)klass{        if (klass == NULL) {        return nil;    }    NSMutableDictionary *results = [[[NSMutableDictionary alloc] init] autorelease];    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) {            const char *propType = getPropertyType(property);            NSString *propertyName = [NSString stringWithUTF8String:propName];            NSString *propertyType = [NSString stringWithUTF8String:propType];            [results setObject:propertyType forKey:propertyName];        }    }    free(properties);    // returning a copy here to make sure the dictionary is immutable    return [NSDictionary dictionaryWithDictionary:results];}@end


@orange80's answer has one problem: It actually doesn't always terminate the string with 0s. This can lead to unexpected results like crashing while trying to convert it to UTF8 (I actually had a pretty annoying crashbug just because of that. Was fun debugging it ^^). I fixed it by actually getting an NSString from the attribute and then calling cStringUsingEncoding:. This works like a charm now. (Also works with ARC, at least for me)

So this is my version of the code now:

// PropertyUtil.h#import @interface PropertyUtil : NSObject+ (NSDictionary *)classPropsFor:(Class)klass;@end// PropertyUtil.m#import "PropertyUtil.h"#import <objc/runtime.h>@implementation PropertyUtilstatic const char *getPropertyType(objc_property_t property) {    const char *attributes = property_getAttributes(property);    //printf("attributes=%s\n", attributes);    char buffer[1 + strlen(attributes)];    strcpy(buffer, attributes);    char *state = buffer, *attribute;    while ((attribute = strsep(&state, ",")) != NULL) {        if (attribute[0] == 'T' && attribute[1] != '@') {            // it's a C primitive type:            /*             if you want a list of what will be returned for these primitives, search online for             "objective-c" "Property Attribute Description Examples"             apple docs list plenty of examples of what you get for int "i", long "l", unsigned "I", struct, etc.             */            NSString *name = [[NSString alloc] initWithBytes:attribute + 1 length:strlen(attribute) - 1 encoding:NSASCIIStringEncoding];            return (const char *)[name cStringUsingEncoding:NSASCIIStringEncoding];        }        else if (attribute[0] == 'T' && attribute[1] == '@' && strlen(attribute) == 2) {            // it's an ObjC id type:            return "id";        }        else if (attribute[0] == 'T' && attribute[1] == '@') {            // it's another ObjC object type:            NSString *name = [[NSString alloc] initWithBytes:attribute + 3 length:strlen(attribute) - 4 encoding:NSASCIIStringEncoding];            return (const char *)[name cStringUsingEncoding:NSASCIIStringEncoding];        }    }    return "";}+ (NSDictionary *)classPropsFor:(Class)klass{    if (klass == NULL) {        return nil;    }    NSMutableDictionary *results = [[NSMutableDictionary 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) {            const char *propType = getPropertyType(property);            NSString *propertyName = [NSString stringWithUTF8String:propName];            NSString *propertyType = [NSString stringWithUTF8String:propType];            [results setObject:propertyType forKey:propertyName];        }    }    free(properties);    // returning a copy here to make sure the dictionary is immutable    return [NSDictionary dictionaryWithDictionary:results];}@end