Динамические свойства Objective-C во время выполнения?

Можно ли создать класс Objective-C, который может иметь произвольное количество динамических свойств во время выполнения?

Я хочу иметь возможность звонить mySpecialClass.anyProperty и перехватить это внутри моего класса, чтобы иметь возможность предоставить собственную реализацию, которая может затем вернуть NSString (например) во время выполнения с повышением исключения. Очевидно, это все должно компилироваться.

Идеально было бы, если бы я мог ссылаться на свои свойства, используя что-то похожее на новый буквальный синтаксис, например mySpecialClass["anyProperty"],

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

3 ответа

Решение

Есть как минимум два способа сделать это.

индексация

использование objectForKeyedSubscript: а также setObject:forKeyedSubscript:

 @property (nonatomic,strong) NSMutableDictionary *properties;

 - (id)objectForKeyedSubscript:(id)key {
      return [[self properties] valueForKey:[NSString stringWithFormat:@"%@",key]];
 }

 - (void)setObject:(id)object forKeyedSubscript:(id <NSCopying>)key {
      [[self properties] setValue:object forKey:[NSString stringWithFormat:@"%@",key]];
 }

 Person *p = [Person new];
 p[@"name"] = @"Jon";
 NSLog(@"%@",p[@"name"]);

resolveInstanceMethod:

Это objc_sendMsg, выполняемый средой выполнения для всех методов:

objc_sendMsg

Если вы посмотрите на дно, у вас есть возможность resolveInstanceMethod:, который позволяет перенаправить вызов метода на один из ваших вариантов. Чтобы ответить на ваш вопрос, вам нужно написать общий метод получения и установки, который ищет значение в словаре ivar:

// generic getter
static id propertyIMP(id self, SEL _cmd) {
    return [[self properties] valueForKey:NSStringFromSelector(_cmd)];
}


// generic setter
static void setPropertyIMP(id self, SEL _cmd, id aValue) {

    id value = [aValue copy];
    NSMutableString *key = [NSStringFromSelector(_cmd) mutableCopy];

    // delete "set" and ":" and lowercase first letter
    [key deleteCharactersInRange:NSMakeRange(0, 3)];
    [key deleteCharactersInRange:NSMakeRange([key length] - 1, 1)];
    NSString *firstChar = [key substringToIndex:1];
    [key replaceCharactersInRange:NSMakeRange(0, 1) withString:[firstChar lowercaseString]];

    [[self properties] setValue:value forKey:key];
}

А затем реализовать resolveInstanceMethod: добавить запрошенный метод в класс.

+ (BOOL)resolveInstanceMethod:(SEL)aSEL {
    if ([NSStringFromSelector(aSEL) hasPrefix:@"set"]) {
        class_addMethod([self class], aSEL, (IMP)setPropertyIMP, "v@:@");
    } else {
        class_addMethod([self class], aSEL,(IMP)propertyIMP, "@@:");
    }
    return YES;
}

Вы также можете сделать это, возвращая NSMethodSignature для метода, который затем оборачивается в NSInvocation и передается forwardInvocation:, но добавление метода происходит быстрее.

Вот суть, которая работает в CodeRunner. Не справляется myClass["anyProperty"] звонки.

Вы спрашиваете разные вещи. Если вы хотите использовать скобочный синтаксис mySpecialClass[@"anyProperty"] на экземплярах вашего класса это очень просто. Просто реализуйте методы:

 - (id)objectForKeyedSubscript:(id)key
 {
      return ###something based on the key argument###
 }

 - (void)setObject:(id)object forKeyedSubscript:(id <NSCopying>)key
 {
      ###set something with object based on key####
 }

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

В противном случае, если вы хотите создать свойства во время выполнения, есть разные способы продолжить, посмотрите на NSObject"s forwardInvocation: метод, или посмотрите на Objective-C Справочник по времени выполнения для функций для динамического изменения класса...

Гийом прав. forwardInvocation: это путь Этот ответ дает некоторые дополнительные подробности: функциональность, подобная method_missing, в target-c (т.е. динамическое делегирование во время выполнения)

Это имеет еще больше деталей: Эквивалент Ruby method_missing в Objective C / iOS

И вот некоторые другие менее известные функции Obj-C, которые могут вам помочь: Скрытые функции Objective-C

Наслаждайтесь!

Другие вопросы по тегам