Exponer/sintetizar propiedades de iVar en Objective c
objective-c properties (2)
Tengo una clase que esencialmente actúa como una clase de envoltura ligera en otra clase. Sostiene esa otra clase como un iVar. Quiero poder exponer ciertas propiedades (bastantes en realidad) del iVar, pero para hacerlo debo escribir cada acceso a la propiedad de la siguiente manera:
- (void) setProperty:(Class *)value{
_iVar.property = value;
}
- (Class *) property{
return _iVar.property;
}
Por supuesto, tengo que hacer esto para cada propiedad, lo cual es un dolor (hay alrededor de 30 de ellas). Me encantaría poder sintetizar esto, pero no he podido descifrar cómo.
¿Es posible sintetizar?
Además, no puedo hacer una subclase ... bueno, podría hacerlo, pero en realidad no es recomendable. La clase iVar es realmente bastante pesada (implementa CoreText). Prefiero escribir los métodos a mano.
Creo que puedes reenviar los mensajes al ivar:
- (void) forwardInvocation: (NSInvocation*) invocation
{
[invocation invokeWithTarget:ivar];
}
- (NSMethodSignature*) methodSignatureForSelector: (SEL) selector
{
NSMethodSignature *our = [super methodSignatureForSelector:selector];
NSMethodSignature *ivars = [ivar methodSignatureForSelector:selector];
return our ? our : ivars;
}
Luego tiene que esconder o falsificar el tipo de su objeto, por ejemplo, lanzando a id
, de lo contrario, el compilador se quejará de que su clase no implementa esos métodos. Por supuesto, sería mejor si pudieras encontrar un mejor diseño que lo hiciera sin esos trucos.
Ok, esta es la solución que encontré ... terminó siendo bastante simple una vez que supiste qué hacer. Primero sobrescriba ''- (id) forwardingTargetForSelector: (SEL) aSelector'' y devuelva el iVar:
- (id) forwardingTargetForSelector:(SEL)aSelector{
return iVar;
}
Cuando el tiempo de ejecución está buscando un método y no puede encontrar uno, llamará a este método para ver si hay otro objeto para reenviar el mensaje. Tenga en cuenta que este método normalmente devuelve nil y si devuelve nil aquí, su programa se bloqueará (que es el comportamiento apropiado).
La segunda parte del problema es silenciar los errores / advertencias del compilador que obtendrá cuando intente enviar un mensaje que no está declarado. Esto se hace fácilmente al declarar una categoría que no implementa.
@interface Class (iVarClassMethods)
@propoperty (strong) Class *property1;
......more properties
@end
Siempre que no implementes una implementación en cualquier lugar, también conocida como @implementation Class (category)
, el compilador no se quejará (supondrá que la implementación está en alguna parte ... ).
Ahora, el único inconveniente que veo es que si cambias cualquiera de las propiedades en la interfaz de la clase iVar, debes asegurarte de actualizar todas las otras clases que usan el método descrito anteriormente; de lo contrario, se bloqueará cuando otra clase intente enviar cuál es ahora el método equivocado (y el compilador no lo advertirá de antemano). Sin embargo, esto puede lograrse. Puede declarar protocolos en una categoría. Entonces, en su lugar, creas un protocolo separado para la clase iVar y mueves los métodos / propiedades que deseas de la clase iVar al protocolo.
@protocol iVarClassProtocol
@propoperty (strong) Class *property1;
......more properties
@end
Agregue ese protocolo a la subclase iVar para que tenga esos métodos declarados a través del protocolo ahora.
@interface iVarClass <iVarClassProtocol>
....other methods/properties you don''t need forwarded
@end
Finalmente, simplemente agregue el protocolo a la categoría. Entonces, en lugar de la categoría mencionada con declaraciones explícitas, tendrás:
@interface Class (iVarClassMethods) <iVarClassProtocol>
@end
Ahora, si necesita cambiar cualquiera de las propiedades / métodos que se van a fowarded, los cambia en el protocolo. El compilador le avisará cuando intente enviar el método incorrecto a la clase de reenvío.