Pregunta

¿Es posible agregar propiedades a un objeto C Objetivo C en tiempo de ejecución?

¿Fue útil?

Solución

Es posible agregar propiedades formales a una clase a través de class_addProperty():

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

Los dos primeros parámetros se explican por sí mismos. El tercer parámetro es una matriz de atributos de propiedad, y cada atributo de propiedad es un par de valores de nombre que sigue a Objective-C tipo codificaciones por propiedades declaradas. Tenga en cuenta que la documentación aún menciona la cadena separada por comas para la codificación de los atributos de la propiedad. Cada segmento en la cadena separada por comas está representado por uno objc_property_attribute_t instancia. Es más, objc_property_attribute_t acepta nombres de clase además del genérico @ Tipo de codificación de id.

Aquí hay un primer borrador de un programa que agrega dinámicamente una propiedad llamada name a una clase que ya tiene una variable de instancia llamada _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]);
    }
}

Su salida (recortada):

Steve
Jobs

Los métodos Getter y Setter deben escribirse con más cuidado, pero esto debería ser suficiente como un ejemplo de cómo agregar dinámicamente una propiedad formal en tiempo de ejecución.

Otros consejos

Si echas un vistazo a NSKeyValueCoding Protocolo, documentado aquí, puedes ver que hay un mensaje llamado:

- (id)valueForUndefinedKey:(NSString *)key

Debe anular ese método para proporcionar su resultado personalizado para la propiedad indefinida especificada. Por supuesto, esto supone que su clase usa el protocolo correspondiente.

Este tipo de enfoque se usa comúnmente para proporcionar un comportamiento desconocido a las clases (por ejemplo, un selector que no existe).

@Properties: no (es decir, usando sintaxis de puntos, etc.). Pero puede agregar almacenamiento usando objetos asociados: ¿Cómo uso objc_setassociatedObject/objc_getassociatedObject dentro de un objeto?.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top