2011-10-19 9 views

Respuesta

60

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

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

Los primeros dos parámetros son fáciles de entender. El tercer parámetro es una matriz de atributos de propiedad, y cada atributo de propiedad es un par nombre-valor que sigue Objective-C type encodings para declared properties. Tenga en cuenta que la documentación aún menciona la cadena separada por comas para la codificación de los atributos de propiedad. Cada segmento en la cadena separada por comas está representado por una instancia objc_property_attribute_t. Además, objc_property_attribute_t acepta nombres de clase además de la codificación de tipo genérica @ de id.

Aquí está 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, "[email protected]:@"); 

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

Su (recortada) de salida:

Steve 
Jobs 

Los métodos getter y setter deberían 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.

+1

class_addProperty devuelve true, pero class_getInstanceVariable siempre devuelve nil. Intenté poner el nombre de la propiedad en lugar del nombre ivar, pero todavía no tuve suerte. ¿Alguna idea de lo que podría ser el problema? – Mercurial

+1

@Bavarious, ¿cómo engañaste al compilador? Quiero decir que [o name] da como resultado el error de compilación 'No se conoce ningún método de instancia para el selector' nombre ''. –

+0

@HiteshSavaliya hace mucho tiempo (antes de ARC) esto era solo posible. hoy en día tendrías que al menos declarar el selector '-name'. – Michael

8

Si se echa un vistazo a NSKeyValueCoding protocolo, documentado here, se puede ver que hay un mensaje llamado:

- (id)valueForUndefinedKey:(NSString *)key 

debe reemplazar ese método para proporcionar el resultado de la medida para la propiedad no definida especificada. Por supuesto, esto supone que su clase utiliza 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).

Cuestiones relacionadas