Мне нужно определить тип свойства объекта (переданного по имени), чтобы выполнить десериализацию из XML. У меня есть некоторые общие псевдокод (однако я не уверен, как выполнить эти сравнения в Objective-C):
id object = [[[Record alloc] init] autorelease];
NSString *element = @"date";
NSString *data = @"2010-10-16";
objc_property_t property = class_getProperty([object class], [element UTF8String]);
const char *attributes = property_getAttributes(property);
char buffer[strlen(attributes) + 1];
strcpy(buffer, attributes);
char *attribute = strtok(buffer, ",");
if (*attribute == 'T') attribute++; else attribute = NULL;
if (attribute == NULL);
else if (strcmp(attribute, "@\"NSDate\"") == 0) [object setValue:[NSDate convertToDate:self.value] forKey:element];
else if (strcmp(attribute, "@\"NSString\"") == 0) [object setValue:[NSString convertToString:self.value] forKey:element];
else if (strcmp(attribute, "@\"NSNumber\"") == 0) [object setValue:[NSNumber convertToNumber:self.value] forKey:element];
Я просмотрел class_getProperty и property_getAttributes , однако я до сих пор не уверен как сделать приведенные выше сравнения.
@ Аруман отвечает правильно, если вы имеете дело с объектами. Позвольте мне предложить несколько альтернатив:
valueForKey:
: Если вы используете [myObject valueForKey: @ "myPropertyName"]
, он вернет объект. Если свойство соответствует некоторому примитиву ( int
, float
, CGRect
и т. Д.), То оно будет помещено для вас в NSNumber.
или NSValue
(при необходимости). Если он возвращается как NSNumber
, вы можете легко извлечь двойное представление ( doubleValue
) и использовать его как NSTimeInterval
для создания NSDate
. Я бы, наверное, порекомендовал этот подход. Частный случай каждого типа. property_getAttributes ()
возвращает char *
, представляющий все атрибуты свойства, и вы можете извлечь тип, выполнив следующие действия:
const char * type = property_getAttributes (class_getProperty ( [собственный класс], "myPropertyName")); NSString * typeString = [NSString stringWithUTF8String: тип]; NSArray * attributes = [typeString componentsSeparatedByString: @ ","]; NSString * typeAttribute = [атрибуты objectAtIndex: 0]; NSString * propertyType = [typeAttribute substringFromIndex: 1]; const char * rawPropertyType = [propertyType UTF8String]; if (strcmp (rawPropertyType, @encode (float)) == 0) { // это поплавок } else if (strcmp (rawPropertyType, @encode (int)) == 0) { // это int } else if (strcmp (rawPropertyType, @encode (id)) == 0) { // это какой-то объект } еще ....
Это педантично более правильно, чем ответ Луи, потому что, хотя большинство типов имеют односимвольную кодировку, они не обязаны . (его предложение предполагает односимвольную кодировку)
Наконец, если вы делаете это в подклассе NSManagedObject
, то я бы посоветовал проверить NSPropertyDescription
.
Судя по этим альтернативам, вы, вероятно, увидите, что самый простой вариант - позволить исполняемой среде установить значение для вас.
редактировать извлечение типа:
Из приведенного выше кода вы можете извлечь имя класса следующим образом:
if ([typeAttribute hasPrefix:@"T@"] && [typeAttribute length] > 1) {
NSString * typeClassName = [typeAttribute substringWithRange:NSMakeRange(3, [typeAttribute length]-4)]; //turns @"NSDate" into NSDate
Class typeClass = NSClassFromString(typeClassName);
if (typeClass != nil) {
[object setValue:[self convertValue:self.value toType:typeClass] forKey:element];
}
}
А затем вместо использования категорий методов класса для преобразования (например, [ NSDate convertToDate:]
) создайте метод для self
, который сделает это за вас и принимает желаемый тип в качестве параметра. Вы могли бы (пока) сделать это так:
- (id) convertValue:(id)value toType:(Class)type {
if (type == [NSDate class]) {
return [NSDate convertToDate:value];
} else if (type == [NSString class]) {
return [NSString convertToString:value];
} ...
}
Хотя часть меня задается вопросом: зачем вам вообще нужно делать это таким образом? Что вы делаете?
Если вас интересуют только классы, вы можете использовать isKindOfClass:
, но если вы хотите иметь дело со скалярами, вы правы, что вам нужно использовать property_getAttributes ()
, он возвращает строку, кодирующую информацию о типе. Ниже представлена основная функция, демонстрирующая, что вам нужно делать. Примеры кодирования строк см. здесь .
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList([object class], &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
char *property_name = property_getName(property);
char *property_type = property_getAttributes(property);
switch(property_type[1]) {
case 'f' : //float
break;
case 's' : //short
break;
case '@' : //ObjC object
//Handle different clases in here
break;
}
}
Очевидно, вам нужно будет добавить все типы и классы, которые вам нужно будет обработать, для этого используются обычные типы ObjC @encode .