Pregunta

¿Puedo interceptar una llamada de método en Objective-C? ¿Cómo?

Editar: La respuesta de Mark Powell me dio una solución parcial, el método -forwardInvocation . Pero la documentación indica que -forwardInvocation solo se invoca cuando a un objeto se le envía un mensaje para el que no tiene un método correspondiente. Me gustaría que se llame a un método en cualquier circunstancia, incluso si el receptor tiene ese selector.

¿Fue útil?

Solución

Lo haces cambiando la llamada al método. Suponiendo que desea capturar todos los lanzamientos en NSTableView:

static IMP gOriginalRelease = nil;
static void newNSTableViewRelease(id self, SEL releaseSelector, ...) {
   NSLog(@"Release called on an NSTableView");
   gOriginalRelease(self, releaseSelector);
}


  //Then somewhere do this:
  gOriginalRelease = class_replaceMethod([NSTableView class], @selector(release), newNSTableViewRelease, "v@:");

Puede obtener más detalles en el tiempo de ejecución de Objective C documentación .

Otros consejos

La intercepción de llamadas de método en Objective-C (asumiendo que es un Objective-C, no una llamada de C) se realiza con una técnica llamada swizzling de método.

Puede encontrar una introducción sobre cómo implementar ese aquí . Para ver un ejemplo de cómo se implementa el método swizzling en un proyecto real, consulte OCMock (un Marco de aislamiento para Objective-C).

El envío de un mensaje en Objective-C se traduce en una llamada de la función objc_msgSend (receptor, selector, argumentos) o una de sus variantes objc_msgSendSuper , objc_msgSend_stret , objc_msgSendSuper_stret .

Si fuera posible cambiar la implementación de estas funciones, podríamos interceptar cualquier mensaje. Desafortunadamente, objc_msgSend es parte del tiempo de ejecución de Objective-C y no se puede anular.

Por google me encontré con un artículo sobre Google Books: Una arquitectura reflexiva para aplicaciones de control de procesos por Charlotte Pii Lunau . El documento introduce un hack al redireccionar el puntero de clase isa de un objeto a una instancia de una clase de MetaObject personalizada. Los mensajes destinados al objeto modificado se envían a la instancia de MetaObject. Como la clase MetaObject no tiene métodos propios, puede responder a la invocación hacia adelante reenviando el mensaje al objeto modificado.

El documento no incluye los fragmentos interesantes del código fuente y no tengo idea de si tal enfoque tendría efectos secundarios en Cocoa. Pero podría ser interesante intentarlo.

Si desea registrar los envíos de mensajes desde el código de su aplicación, la sugerencia -forwardingTargetForSelector: es parte de la solución.
Envuelve tu objeto:

@interface Interceptor : NSObject
@property (nonatomic, retain) id interceptedTarget;
@end

@implementation Interceptor
@synthesize interceptedTarget=_interceptedTarget;

- (void)dealloc {
    [_interceptedTarget release];
    [super dealloc];
}

- (id)forwardingTargetForSelector:(SEL)aSelector {
    NSLog(@"Intercepting %@", NSStringFromSelector(aSelector));
    return self.interceptedTarget;
} 

@end

Ahora haz algo como esto:

Interceptor *i = [[[Interceptor alloc] init] autorelease];
NSFetchedResultsController *controller = [self setupFetchedResultsController];
i.interceptedTarget = controller;
controller = (NSFetchedResultsController *)i;

y tendrás un registro de mensajes enviados. Tenga en cuenta que los envíos enviados desde el objeto interceptado no se interceptarán, ya que se enviarán utilizando el puntero "self" del objeto original.

Si solo desea registrar mensajes llamados desde el exterior (generalmente llamados de delegados; para ver qué tipo de mensajes, cuándo, etc.), puede anular responsToSelector de esta manera:

- (BOOL)respondsToSelector:(SEL)aSelector {
NSLog(@"respondsToSelector called for '%@'", NSStringFromSelector(aSelector));

// look up, if a method is implemented
if([[self class] instancesRespondToSelector:aSelector]) return YES;

return NO;

}

Cree una subclase de NSProxy e implementa -forwardInvocation: y -methodSignatureForSelector: (o -forwardingTargetFor Selector: , si simplemente lo está dirigiendo a un segundo objeto en lugar de jugar con el método usted mismo.

NSProxy es una clase diseñada para implementar -forwardInvocation: en. Tiene algunos métodos, pero en su mayoría no quieres que los atrapen. Por ejemplo, capturar los métodos de conteo de referencias evitaría que el proxy se desasigne, excepto en la recolección de basura. Pero si hay métodos específicos en NSProxy que absolutamente necesita reenviar, puede anular ese método específicamente y llamar a -forwardInvocation: manualmente. Tenga en cuenta que simplemente porque un método esté listado en la documentación de NSProxy no significa que NSProxy lo implemente, simplemente que se espera que todos los objetos con proxy lo tengan.

Si esto no funciona, proporcione detalles adicionales sobre su situación.

Tal vez quiera el método -forwardInvocation de NSObject . Esto le permite capturar un mensaje, redirigirlo y luego reenviarlo.

Puedes mezclar la llamada al método con uno de los tuyos, que hace lo que quieras hacer en " intercepción " y llamadas a través de la implementación original. Swizzling se realiza con class_replaceMethod ().

Una llamada de método, no. Un mensaje enviado, sí, pero tendrá que ser mucho más descriptivo si quiere una buena respuesta sobre cómo hacerlo.

Para hacer algo cuando se llama a un método, puede probar un enfoque basado en eventos. Entonces, cuando se llama al método, se emite un evento, que es escuchado por cualquier oyente. No soy muy bueno con el objetivo C, pero acabo de descubrir algo similar con NSNotificationCenter en Cocoa.

Pero si por " interceptar " te refieres a " parada " ;, entonces tal vez necesites más lógica para decidir si se debe llamar al método.

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