В 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, однако я до сих пор не уверен, как выполнить приведенные выше сравнения.

Ответы

Ответ 1

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

  • valueForKey:: Если вы используете [myObject valueForKey:@"myPropertyName"], он вернет объект. Если свойство соответствует некоторому примитивному (int, float, CGRect и т.д.), Тогда он будет помещен для вас в NSNumber или NSValue (в зависимости от ситуации). Если он возвращается как NSNumber, вы можете легко извлечь двойное представление (doubleValue) и использовать его как NSTimeInterval для создания NSDate. Я бы, вероятно, рекомендовал этот подход.
  • Специальный чехол каждый тип. property_getAttributes() возвращает a char*, представляющий все атрибуты свойства, и вы можете извлечь этот тип, выполнив следующее:

    const char * type = property_getAttributes(class_getProperty([self class], "myPropertyName"));
    NSString * typeString = [NSString stringWithUTF8String:type];
    NSArray * attributes = [typeString componentsSeparatedByString:@","];
    NSString * typeAttribute = [attributes objectAtIndex:0];
    NSString * propertyType = [typeAttribute substringFromIndex:1];
    const char * rawPropertyType = [propertyType UTF8String];
    
    if (strcmp(rawPropertyType, @encode(float)) == 0) {
      //it a float
    } else if (strcmp(rawPropertyType, @encode(int)) == 0) {
      //it an int
    } else if (strcmp(rawPropertyType, @encode(id)) == 0) {
      //it some sort of object
    } else ....

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

  • Наконец, если вы делаете это в подклассе NSManagedObject, я бы рекомендовал проверить NSPropertyDescription.

Из этих альтернатив вы, вероятно, увидите, что для того, чтобы ящик времени выполнения имел для вас значение, вероятно, является самым простым.

изменить извлечение типа:

Из приведенного выше кода вы можете извлечь имя класса следующим образом:

if ([typeAttribute hasPrefix:@"[email protected]"] && [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];
  } ...
}

Часть меня удивляется, почему: зачем вам так делать? Что вы делаете?

Ответ 2

Если вы только заботитесь о классах, вы можете использовать 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 types.

Ответ 3

[object isKindOfClass:[NSDate class]] и т.д.