В Objective-C Определить, является ли свойство типом int, float, double, NSString, NSDate, NSNumber и т. Д.

Мне нужно определить тип свойства объекта (переданного по имени), чтобы выполнить десериализацию из 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 , однако я до сих пор не уверен как сделать приведенные выше сравнения.

14
задан Kevin Sylvestre 17 August 2010 в 00:13
поделиться

3 ответа

@ Аруман отвечает правильно, если вы имеете дело с объектами. Позвольте мне предложить несколько альтернатив:

  1. valueForKey: : Если вы используете [myObject valueForKey: @ "myPropertyName"] , он вернет объект. Если свойство соответствует некоторому примитиву ( int , float , CGRect и т. Д.), То оно будет помещено для вас в NSNumber. или NSValue (при необходимости). Если он возвращается как NSNumber , вы можете легко извлечь двойное представление ( doubleValue ) и использовать его как NSTimeInterval для создания NSDate . Я бы, наверное, порекомендовал этот подход.
  2. Частный случай каждого типа. 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) {
     // это какой-то объект
    } еще ....

    Это педантично более правильно, чем ответ Луи, потому что, хотя большинство типов имеют односимвольную кодировку, они не обязаны . (его предложение предполагает односимвольную кодировку)

  3. Наконец, если вы делаете это в подклассе 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];
  } ...
}

Хотя часть меня задается вопросом: зачем вам вообще нужно делать это таким образом? Что вы делаете?

37
ответ дан 1 December 2019 в 06:59
поделиться

Если вас интересуют только классы, вы можете использовать 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 .

3
ответ дан 1 December 2019 в 06:59
поделиться

[объект isKindOfClass: [NSDate class]] и т. Д.

0
ответ дан 1 December 2019 в 06:59
поделиться