Написание моих собственных свойств @dynamic в Какао

Предположим (ради аргумента), что у меня есть класс представления, который содержит NSDictionary. Мне нужен целый набор свойств, все из которых имеют доступ к членам этого словаря.

Например, я хочу @property NSString * title и @property NSString * author .

Для каждого из этих свойств реализация одна и та же: для метода получения вызовите [dictionary objectForKey: propertyName]; , и для установщика сделайте то же самое с setObject: forKey:.

Потребовалось бы много времени и много кода для копирования и вставки, чтобы написать все эти методы. Есть ли способ генерировать их все автоматически, как это делают Core Data со свойствами @dynamic для подклассов NSManagedObject? Чтобы было ясно, я хочу, чтобы это средство доступа было доступно только для свойств, которые я определяю в заголовке, а не просто для произвольного ключа.

Я встречал valueForUndefinedKey: как часть кодирования значения ключа, которое может обрабатывать получатели, но я Я не совсем уверен, является ли это лучшим способом.

Мне нужно, чтобы это были явные свойства, чтобы я мог связать их в Интерфейсном Разработчике: я в конечном итоге планирую написать палитру IB для этого представления.

( Кстати, я знаю, что мой пример использования NSDictionary для их хранения немного надуман. Я на самом деле пишу подкласс WebView, и свойства будут ссылаться на идентификаторы элементов HTML, но это не важно для логики моего вопроса!)

16
задан Amy Worrall 24 August 2010 в 19:37
поделиться

4 ответа

Мне удалось решить эту проблему самостоятельно после того, как пролился на документацию среды выполнения objective-c.

Я реализовал этот метод класса:

+ (BOOL) resolveInstanceMethod:(SEL)aSEL
{
    NSString *method = NSStringFromSelector(aSEL);

    if ([method hasPrefix:@"set"])
    {
        class_addMethod([self class], aSEL, (IMP) accessorSetter, "v@:@");
        return YES;
    }
    else
    {
        class_addMethod([self class], aSEL, (IMP) accessorGetter, "@@:");
        return YES;
    }
    return [super resolveInstanceMethod:aSEL];
}

За ним следует пара функций C:

NSString* accessorGetter(id self, SEL _cmd)
{
    NSString *method = NSStringFromSelector(_cmd);
    // Return the value of whatever key based on the method name
}

void accessorSetter(id self, SEL _cmd, NSString* newValue)
{
    NSString *method = NSStringFromSelector(_cmd);

    // remove set
    NSString *anID = [[[method stringByReplacingCharactersInRange:NSMakeRange(0, 3) withString:@""] lowercaseString] stringByReplacingOccurrencesOfString:@":" withString:@""];

    // Set value of the key anID to newValue
}

Поскольку этот код пытается реализовать любой метод, который вызывается в классе и еще не реализован, это вызовет проблемы, если кто-то попытается Вы называете то, чего ожидаете от заметки. Я планирую добавить некоторую проверку здравомыслия, чтобы убедиться, что имена совпадают с тем, что я ожидаю.

17
ответ дан 30 November 2019 в 22:09
поделиться

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

0
ответ дан 30 November 2019 в 22:09
поделиться

Вы можете использовать сочетание предложенных параметров:

  • используйте ключевое слово @dynamic
  • перезаписать valueForKey: и setValue:forKey: для доступа к словарю
  • используйте метод API отражения objective-c class_getProperty и проверьте его на нулевое значение. Если это не ноль, ваш класс имеет такое свойство. Это не так, если это так.
  • затем вызовите метод super в тех случаях, когда такое свойство не существует.

Надеюсь, это поможет. Может показаться немного халтурным (с использованием рефлексии), но на самом деле это очень гибкое и к тому же абсолютно «легальное» решение проблемы...

PS: способ coredata возможен, но в вашем случае это было бы полным излишеством...

2
ответ дан 30 November 2019 в 22:09
поделиться

Подружиться с Макро? Это может быть не на 100% правильно.

#define propertyForKey(key, type) \
    - (void) set##key: (type) key; \
    - (type) key;

#define synthesizeForKey(key, type) \
    - (void) set##key: (type) key \
    { \
         [dictionary setObject];// or whatever \
    } \
    - (type) key { return [dictionary objectForKey: key]; }
2
ответ дан 30 November 2019 в 22:09
поделиться