В Objective-C Определите, является ли свойство типом int, float, double, NSString, NSDate, NSNumber и т. Д. - PullRequest
14 голосов
/ 17 августа 2010

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

Ответы [ 3 ]

37 голосов
/ 17 августа 2010

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

  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([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's a float
    } else if (strcmp(rawPropertyType, @encode(int)) == 0) {
      //it's an int
    } else if (strcmp(rawPropertyType, @encode(id)) == 0) {
      //it's some sort of object
    } else ....

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

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

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

edit извлечение типа:

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

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];
  } ...
}

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

3 голосов
/ 17 августа 2010

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

0 голосов
/ 17 августа 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...