Obturadores y sets dynamics con el objective C

Estoy en una situación en la que quiero generar dinámicamente getters y setters para una class en time de ejecución (de manera similar a lo que hace NSManagedObject detrás de escena). Desde mi entendimiento, esto es posible utilizando resolveInstanceMethod: en una class específica. En este punto, deberías usar class_addMethod para agregar dinámicamente el método basado en el selector. Entiendo esto a nivel teórico, pero no he profundizado mucho en el time de ejecución de obj-c, así que tenía curiosidad de ver si había grandes ejemplos de cómo hacerlo. La mayor parte de mi conocimiento proviene de este artículo:

http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtDynamicResolution.html

¿Alguna idea / ejemplo?

La única buena conversación que sé es en la publicación de blog de Mike Ash. No es tan difícil, en realidad.

Una vez tuve que dividir una gran subclass NSManagedObject en dos, pero decidí mantener el hecho un detalle de implementación para que no tenga que volver a escribir otras partes de mi aplicación. Por lo tanto, necesitaba sintetizar getter y setter que envía [self foo] a [self.data foo] , automáticamente.

Para lograr eso, hice lo siguiente:

  1. Prepare el nuevo método, ya en mi class.

     - (id)_getter_ { return objc_msgSend(self.data, _cmd); } - (void)_setter_:(id)value { objc_msgSend(self.data, _cmd,value); } 

    Tenga en count que _cmd tiene el selector en él. Normalmente, _cmd es @selector(_getter_) o @selector(_setter_) en estos methods, pero voy a conectar la implementación de _getter_ como la implementación de foo . Entonces, _cmd contiene @selector(foo) y, por lo tanto, llama a self.data 's foo .

  2. Escribir un método genérico de síntesis:

     +(void)synthesizeForwarder:(NSString*)getterName { NSString*setterName=[NSString stringWithFormat:@"set%@%@:", [[getterName substringToIndex:1] uppercaseString],[getterName substringFromIndex:1]]; Method getter=class_getInstanceMethod(self, @selector(_getter_)); class_addMethod(self, NSSelectorFromString(getterName), method_getImplementation(getter), method_getTypeEncoding(getter)); Method setter=class_getInstanceMethod(self, @selector(_setter_:)); class_addMethod(self, NSSelectorFromString(setterName), method_getImplementation(setter), method_getTypeEncoding(setter)); } 

    Tenga en count que este es un método de class. Así que self para la class. Tenga en count también que no hice codificaciones de tipo hardcode (lo que indica a Runtime de Objective-C cuáles son los arguments del método particular). La syntax de las codificaciones de tipo está documentada, pero la construcción a mano es muy propensa a errores; Perdí unos días así hasta que Mike Ash me dijo que lo detuviera. Generarlo usando un método existente.

  3. Genere reenviadores lo más pronto posible:

      +(void)load { for(NSString*selectorName in [NSArray arrayWithObjects:@"foo", @"bar", @"baz",nil]){ [self synthesizeForwarder:selectorName]; } } 

    Esto genera foo , setFoo: bar , setBar: y baz , setBaz:

¡Espero que esto ayude!

Otro ejemplo es uno que escribí, llamado DynamicStorage , disponible aquí:

https://github.com/davedelong/Demos

El ímpetu principal detrás de esto fue esta pregunta , que estaba preguntando cómo usar un NSMutableDictionary como almacén de respaldo para cualquier object ivar. Escribí una class que generará getters y setters para cualquier @property , respetando cosas como un nombre de getter / setter personalizado, la política de gestión de memory de objects, etc. Lo bueno de esto es que está usando imp_implementationWithBlock() para que solo tenga para calcular el nombre de propiedad apropiado una vez (y luego lo captura y lo guarda como parte del bloque).