Подтвердить что ты не робот

Написание собственных @динамических свойств в Cocoa

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

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

Для каждого из этих свойств реализация одинакова: для getter, call [dictionary objectForKey:propertyName]; и для setter делать то же самое с setObject: forKey:.

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

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

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

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

4b9b3361

Ответ 1

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

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

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

    if ([method hasPrefix:@"set"])
    {
        class_addMethod([self class], aSEL, (IMP) accessorSetter, "[email protected]:@");
        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
}

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

Ответ 2

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

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

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

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

Ответ 3

Подружитесь с макросом? Это не может быть на 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]; }

Ответ 4

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

Ответ 5

Есть хороший блог с примером кода с более надежными проверками динамических свойств на https://tobias-kraentzer.de/2013/05/15/dynamic-properties-in-objective-c/ также очень хороший ответ SO в Objective-C динамические свойства во время выполнения?.

Несколько точек ответа. Возможно, хотите объявить @property в интерфейсе, чтобы позволить typeahead также объявлять свойства динамическими в реализации.