Динамические свойства 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, выполняемый средой выполнения для всех методов:
Если вы посмотрите на дно, у вас есть возможность 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
Наслаждайтесь!