2012-02-12 10 views
7

¿Hay alguna forma de monitorear la llamada a un método especial y recibir una notificación? Me gustaría tener una función llamada "Observer" cuando se invoca [object someMethod]. Sé que puedo monitorear el cambio de valor de un objeto usando el objetivo c KVO, pero no me dice que la pila de llamadas (qué funciones) causó el cambio del valor del objeto. ¿Hay algo similar a KVO, pero para los métodos, donde puedo monitorear las llamadas de función? ¿Alguna sugerencia sobre cómo implementar tal característica si esto no es posible con el marco existente? ¡Gracias!Objetivo C: recibir una notificación cuando se llame a un método

Respuesta

1

En un related question, Bill Bumgarner observa (sin juego de palabras) que esto no es posible para el caso general.

Sugeriría que si necesita conocer a la persona que llama debido a su diseño de API, el mejor enfoque es modificar su método para tomar un parámetro "remitente" (como lo hacen muchos de los métodos marco, incluyendo cada IBAction) . Entonces es responsabilidad del que llama pasar al método.

Esto tiene la ventaja adicional de permitir patrones más complejos. Podría imaginar, por ejemplo, un objeto proxy legítimamente queriendo pasar otro objeto en lugar de uno mismo. (Obviamente, esto tendría que hacerse con cuidado y previsión, sin embargo).

9

Supongo que no controlas la implementación de la clase cuyo método quieres interceptar y, por lo tanto, cambias la API como @Conrad Shultz sugiere que no es factible. Hay muchos otros enfoques que vienen a la mente.

El primero es el método swizzling. La forma en que esto funciona es agregar un método con la misma firma a la clase utilizando una categoría, luego intercambia las implementaciones del método que desea interceptar y el método que ha agregado. El método que agregue llamará a los enganches que desee antes y/o después de llamar a "sí mismo". Como intercambia las implementaciones, llamar a "sí mismo" realmente llama al método original. Hay muchas buenas explicaciones del método que se desvanece por ahí. Here's one.

Otra opción podría ser envolver el objeto en un NSProxy y pasar llamadas usando -forwardInvocation. De nuevo, hay muchos recursos que explican los proxies y el reenvío de llamadas en detalle, por lo que no lo repetiré aquí. Here's one. Este enfoque está limitado, ya que debe tener el acceso necesario para intercambiar su proxy por el objeto real. Si el objeto se crea de forma privada dentro de alguna API, entonces este enfoque no será útil.

Otro enfoque es hacer lo que hace KVO. La Observación de valores clave funciona creando una subclase de una clase en tiempo de ejecución y luego haciendo lo que se llama isa -swing para cambiar la clase de la instancia existente a la nueva subclase dinámica. No hay ninguna razón para no crear una subclase generada en tiempo de ejecución de la clase cuyo método de instancia desea interceptar, y luego hacer que su implementación de la subclase generada en tiempo de ejecución llame a cualquier otro gancho antes y/o después de llamar a la superclase (real) implementación del método. Esto tiene el potencial de permitirle interceptar más de un solo método más fácilmente, pero no va a haber una manera directa de interceptar métodos arbitrarios con firmas arbitrarias, porque debe encontrar una manera de llamar a sus ganchos sin destruir el apilar y luego vector en la implementación primaria. La interceptación de métodos arbitrarios con firmas arbitrarias de esta manera probablemente implicaría escribir los trampolines en el ensamblado y luego realizar una llamada de cola en la implementación real, como hace objc_msgSend. Aquí hay un good article on runtime subclassing.

Para la perspectiva histórica: también solía haber una característica llamada poseAsClass que también permitiría esto, pero se ha eliminado, creo que desde SnowLeopard-ish timeframe, por lo que es probable que no sea un comienzo.

Cualquiera de estas opciones tendrá implicaciones de rendimiento y son extremadamente "inteligentes". No digo eso para darme una palmadita en la espalda; no presenté ninguno de estos, solo los regurgité. Pero he notado a través de los años que cuando una solución se siente demasiado "inteligente", es una señal reveladora de que me dirijo a un desastre eventual. Dicho de otra manera, si la API con la que trabaja no le proporciona los medios para interceptar estas llamadas a métodos, probablemente sea una señal de que debe abordar el problema desde un ángulo diferente. Pero su kilometraje puede variar, obviamente.

Cuestiones relacionadas