Как проверить наличие и тип свойства на основе типизированного ключа NSString?

В моем стремлении обновить модель Core Data в моем проекте iOS я запрашиваю у сервера объекты JSON, которые в некоторой степени соответствуют управляемым объектам моей модели. Конечным результатом, к которому я стремлюсь, является надежное решение для обновления из вывода JSON.

Для примеров в этом вопросе я назову управляемый объект основных данных existingObj и входящий десериализованный словарь JSON updateDict. Сложная часть связана с этими фактами:

  1. Не все свойства existingObj присутствуют в updateDict
  2. Не все свойства updateDict доступны в extistingObj.
  3. Не все типы свойств existingObj соответствуют десериализованным свойствам JSON. (для некоторых строк может потребоваться специальная оболочка Objective-C).
  4. updateDict может содержать значения для ключей, которые не инициализированы (nil) в existingObj.

Это означает, что при повторении обновленных словарей должно быть некоторое тестирование свойств туда и обратно. Сначала я должен проверить, существуют ли свойства updateDict в existingObj, затем я устанавливаю значение с помощью KVC, например так:

// key is an NSString, e.g. @"displayName"
if ([existingObj respondsToSelector:NSSelectorFromString(key)) {
    [existingObj setValue:[updateDict objectForKey:key] forKey:key];
}

Хотя эта часть работает, мне не нравится тот факт, что я на самом деле тестирую displayName в качестве геттера, в то время как я собираюсь вызвать сеттер setDisplayName: (косвенно через KVC). Я бы предпочел что-то вроде [existingObj hasWritablePropertyWithName:key], но что-то, что делает это, я не могу найти.

Это приводит к подвопросу A: как можно проверить установщик свойств, если у вас есть только имя свойства?

В следующей части я хотел бы автоматизировать идентификацию свойств на основе их типов. Если и updateDict, и existingObj имеют NSString для ключа @"displayName", установка нового значения проста. Однако, если updateDict содержит NSString для ключа @"color", который является @"niceShadeOfGreen", я хотел бы преобразовать его в правильный экземпляр UIColor. Но как мне проверить тип принимающего свойства в existingObj, чтобы я знал, когда преобразовывать значения, а когда просто присваивать? Я надеялся на что-то вроде typeOfSelector:

if ([existingObj typeOfSelector:sel] == [[updateDict objectForKey:key] class]) {
     // regular assignment
} else {
     // perform custom assignment
}

Конечно, это фиктивный код. Я не могу полагаться на проверку типа значения existingObj-свойства, так как оно может быть унифицированным или nil.

Подвопрос Б. Как проверить тип свойства, если у вас есть только название свойства?

Думаю, это все. Я подумал, что это, должно быть, дубликат чего-то, что уже было здесь, но я не смог его найти. Может быть, вы, ребята, можете?
Ура, EP.

P.S. Если у вас есть лучший способ синхронизации пользовательских объектов Objective-C с десериализованными объектами JSON, пожалуйста, поделитесь! В конце концов, важен результат.


person epologee    schedule 09.02.2011    source источник
comment
Я предлагаю вам просто запросить у updateDict ключи, которые вы знаете, и проигнорировать все остальные.   -  person Costique    schedule 10.02.2011
comment
Судя по приведенному выше примеру, это действительно жизнеспособный вариант. Однако в реальном сценарии это сделало бы изменения управляемого объекта и обновления очень сложными. Объекты состоят из более чем 30 ключей/свойств, большинство из которых — NSString или NSNumber. Если есть решение поставленного выше вопроса, синхронизация не потребует изменений, когда примитивные типы добавляются или изменяются с обеих сторон.   -  person epologee    schedule 10.02.2011


Ответы (1)


Если вы хотите запросить, есть ли у объекта установщик для данного ключа KVC с именем key, который соответствует объявленному свойству, вам нужно проверить, отвечает ли он на метод селектора с именем setKey: (начинается с set, первый символ в key пишется с большой буквы, добавить двоеточие в конце). Например,

NSString *key = @"displayName";
NSString *setterStr = [NSString stringWithFormat:@"set%@%@:",
                       [[key substringToIndex:1] capitalizedString],
                      [key substringFromIndex:1]];

if ([obj respondsToSelector:NSSelectorFromString(setterStr)]) {
    NSLog(@"found the setter!");
    [obj setValue:someValue forKey:key];
}

Два замечания:

  • Несмотря на то, что у свойств могут быть установщики с именами, которые не соответствуют шаблону, описанному выше, они не будут совместимы с KVC, поэтому безопасно проверять наличие set<Key>:, поскольку вы используете KVC для установки соответствующего значения.

  • KVC использует не только метод установки. Если он не находит метод установки, он проверяет, разрешает ли класс прямой доступ к переменным экземпляра, и, если да, использует переменную экземпляра для установки значения. Кроме того, если метод установки или переменная экземпляра не найдены, он отправляет -setValue:forUndefinedKey: получателю, чей класс мог переопределить стандартную реализацию, выбрасывающую исключение. Это описано в Руководство по кодированию значений ключа. Тем не менее, если вы всегда используете свойства, проверка метода установки должна быть безопасной.

Что касается вашего второго вопроса, невозможно запросить среду выполнения, чтобы узнать фактический класс свойства Objective-C. С точки зрения среды выполнения существует кодировка типа, специфичная для реализации, для свойства и общие типы (например, параметры метода/возвратные типы). Эта кодировка типа использует одну кодировку (а именно @) для любого объекта Objective-C, поэтому кодировка типа свойства NSString такая же, как кодировка типа свойства UIColor, поскольку они оба являются классами Objective-C.

Если вам нужна эта функциональность, одной из альтернатив является обработка ваших классов и добавление метода класса, который возвращает словарь с ключами и соответствующими типами для каждого свойства (или тех, которые вас интересуют), объявленных в этом классе и суперклассах, или, может быть, своего рода язык описания. Вам придется делать это самостоятельно и полагаться на информацию, недоступную во время выполнения.

person Community    schedule 10.02.2011
comment
Очень ценный вклад @Bavarious. Я не подумал о словаре с соответствующими типами, это самое элегантное решение! Разве это не обычная проблема при обновлении локальной модели из ответа сервера в JSON? Есть ли лучшие решения для такой синхронизации? Привет, ИП. - person epologee; 10.02.2011
comment
На самом деле это была бы тема для следующего вопроса, я пока закрою этот. Спасибо! - person epologee; 10.02.2011
comment
действительно здорово, но как это будет работать (эффективно) для ключевых путей? - person Kevin R; 08.01.2014