その後のその後

iOSエンジニア 堤 修一のブログ github.com/shu223

オブジェクトが持つプロパティの型と名前のリストを取得する

クラス/オブジェクトが持つプロパティ情報を取得するには、Objective-C のランタイムにある、

objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount);

を使用します。


また objc_property_t 型のプロパティ情報からプロパティ名を取得するには、同じくobjcランタイム内の

const char *property_getName(objc_property_t property);

を使用します。

プロパティ名一覧を返すメソッドの実装例

まずランタイムのヘッダをインポート。

#import "objc/runtime.h"


こんな感じでプロパティ名一覧を返すメソッドを実装できます。

- (NSArray *)propertyNames {
    unsigned int outCount, i;
    objc_property_t *properties = class_copyPropertyList([self class], &outCount);
    NSMutableArray *arr = [NSMutableArray arrayWithCapacity:0];
    for(i = 0; i < outCount; i++) {
        objc_property_t property = properties[i];
        const char *propName = property_getName(property);
        if(propName) {
            NSString *propertyName = [NSString stringWithCString:propName];
            [arr addObject:propertyName];
        }
    }
    free(properties);
    
    return arr;
}

プロパティ名と型を返すメソッドの実装例

まず、objc_property_t 型のプロパティ情報から型名を取り出すメソッドを実装します。
こちらのページの2つ目の回答に載っているコードほぼそのまんまです。(1つ目の回答のコードだとエラーになる場合があります)

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' && attribute[1] != '@') {
            return (const char *)[[NSData dataWithBytes:(attribute + 1) length:strlen(attribute) - 1] bytes];
        }        
        else if (attribute[0] == 'T' && attribute[1] == '@' && strlen(attribute) == 2) {
            return "id";
        }
        else if (attribute[0] == 'T' && attribute[1] == '@') {
            return (const char *)[[NSData dataWithBytes:(attribute + 3) length:strlen(attribute) - 4] bytes];
        }
    }
    return "";
}

で、下記のように、キーとしてプロパティ名、値として型名を持つ NSDictionary 型でプロパティ一覧を返すメソッドを実装できます。

- (NSDictionary *)properties {
    unsigned int outCount, i;
    objc_property_t *props = class_copyPropertyList([self class], &outCount);
    NSMutableDictionary *dic = [NSMutableDictionary dictionaryWithCapacity:0];
    for(i = 0; i < outCount; i++) {
        objc_property_t property = props[i];
        const char *propName = property_getName(property);
        if(propName) {
            const char *propType = getPropertyType(property);
            NSString *propertyName = [NSString stringWithCString:propName];
            NSString *propertyType = [NSString stringWithCString:propType];
            [dic setObject:propertyType forKey:propertyName];
        }
    }
    free(props);
    
    return dic;
}