Как я могу добавить свойства к объекту во время выполнения? - PullRequest
40 голосов
/ 19 октября 2011

Можно ли добавить свойства к объекту Objective C во время выполнения?

Ответы [ 3 ]

60 голосов
/ 20 октября 2011

Можно добавить формальные свойства в класс через class_addProperty():

BOOL class_addProperty(Class cls,
    const char *name,
    const objc_property_attribute_t *attributes,
    unsigned int attributeCount)

Первые два параметра говорят сами за себя. Третий параметр представляет собой массив атрибутов свойств, а каждый атрибут свойства представляет собой пару имя-значение, которая соответствует кодировкам типа Objective-C для объявленных свойств . Обратите внимание, что в документации по-прежнему упоминается разделенная запятыми строка для кодирования атрибутов свойства. Каждый сегмент в строке через запятую представлен одним экземпляром objc_property_attribute_t. Кроме того, objc_property_attribute_t принимает имена классов, кроме общей кодировки @ типа id.

Вот первый черновик программы, которая динамически добавляет свойство с именем name в класс, в котором уже есть переменная экземпляра с именем _privateName:

#include <objc/runtime.h>
#import <Foundation/Foundation.h>

@interface SomeClass : NSObject {
    NSString *_privateName;
}
@end

@implementation SomeClass
- (id)init {
    self = [super init];
    if (self) _privateName = @"Steve";
    return self;
}
@end

NSString *nameGetter(id self, SEL _cmd) {
    Ivar ivar = class_getInstanceVariable([SomeClass class], "_privateName");
    return object_getIvar(self, ivar);
}

void nameSetter(id self, SEL _cmd, NSString *newName) {
    Ivar ivar = class_getInstanceVariable([SomeClass class], "_privateName");
    id oldName = object_getIvar(self, ivar);
    if (oldName != newName) object_setIvar(self, ivar, [newName copy]);
}

int main(void) {
    @autoreleasepool {
        objc_property_attribute_t type = { "T", "@\"NSString\"" };
        objc_property_attribute_t ownership = { "C", "" }; // C = copy
        objc_property_attribute_t backingivar  = { "V", "_privateName" };
        objc_property_attribute_t attrs[] = { type, ownership, backingivar };
        class_addProperty([SomeClass class], "name", attrs, 3);
        class_addMethod([SomeClass class], @selector(name), (IMP)nameGetter, "@@:");
        class_addMethod([SomeClass class], @selector(setName:), (IMP)nameSetter, "v@:@");

        id o = [SomeClass new];
        NSLog(@"%@", [o name]);
        [o setName:@"Jobs"];
        NSLog(@"%@", [o name]);
    }
}

Его (усеченный) вывод:

Steve
Jobs

Методы получения и установки должны быть написаны более тщательно, но этого должно быть достаточно в качестве примера того, как динамически добавлять формальное свойство во время выполнения.

9 голосов
/ 19 октября 2011

Если вы посмотрите на NSKeyValueCoding протокол, задокументированный здесь , вы увидите, что есть сообщение под названием:

- (id)valueForUndefinedKey:(NSString *)key

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

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

4 голосов
/ 19 октября 2011

@ properties - нет (т.е. с использованием точечного синтаксиса и т. Д.).Но вы можете добавить хранилище, используя связанные объекты: Как использовать objc_setAssociatedObject / objc_getAssociatedObject внутри объекта? .

...