232 votos

¿Cuáles son los peligros del método tragando en Objective C?

He escuchado a personas afirman que el método swizzling es una práctica peligrosa. Incluso el nombre swizzling sugests que es un poco tramposo.

Método Swizzling es la modificación de la asignación, de modo que llamar selector de Una realidad invocar la aplicación B. Un uso de esta es extender el comportamiento de código cerrado clases.

Podemos formalizar los riesgos, de forma que cualquier persona que es decidir si va a utilizar swizzling pueda tomar una decisión informada si vale la pena para lo que están tratando de hacer.

Por ejemplo.

  • Nomenclatura de las Colisiones: Si la clase más tarde amplía su funcionalidad para incluir el nombre del método que usted ha añadido, va a causar una manera enorme de problemas. Reducir el riesgo por la sensatez de nomenclatura swizzled métodos.

433voto

wbyoung Puntos 9428

Creo que esa es una gran pregunta, y es una vergüenza que en lugar de abordar la cuestión real, la mayoría de las respuestas han eludido la cuestión y simplemente dijo : no utilizar swizzling.

Utilizando el método candente es como el uso de cuchillos afilados en la cocina. Algunas personas tienen miedo a los cuchillos afilados porque piensan que van a cortarse el mal, pero la verdad es que los cuchillos afilados son más seguras.

Método swizzling puede ser utilizado para escribir mejor, más eficiente, más un código fácil de mantener. También puede ser objeto de abusos y llevar a los horribles errores.

Antecedentes

Como con todos los diseño de patrones, si somos plenamente conscientes de las consecuencias de los patrones, que son capaces de tomar decisiones más informadas sobre si usarla o no. Los únicos son un buen ejemplo de algo que es bastante polémico, y por una buena razón - están realmente difícil de aplicar correctamente. Mucha gente todavía opta por utilizar singleton, aunque. El mismo puede ser dicho acerca de swizzling. Usted debe formarse su propia opinión una vez que usted entiende completamente el bien y el mal.

Discusión

Aquí están algunas de las dificultades de método swizzling:

  • Método swizzling no es atómico
  • Los cambios de comportamiento de las naciones unidas de propiedad del código
  • Posibles conflictos de nomenclatura
  • Swizzling cambia el método de los argumentos de
  • El orden de los asuntos swizzles
  • Difícil de entender (se ve recursiva)
  • Difícil de depurar

Estos puntos son válidos, y en dirigiéndose a ellos podemos mejorar nuestra comprensión del método swizzling así como la metodología utilizada para lograr el resultado. Voy a tomar uno a la vez.

Método swizzling no es atómico

Todavía tengo que ver una implementación del método swizzling que es seguro utilizar simultáneamente1. En realidad este no es un problema en el 95% de los casos que te gustaría utilizar el método swizzling. Generalmente, usted simplemente desea reemplazar la aplicación de un método, y desea que la aplicación para ser usada por toda la duración de su programa. Esto significa que usted debe hacer su método swizzling en +(void)load. La load de la clase se ejecuta el método en serie en el inicio de su aplicación. Usted no tiene ningún problema con la concurrencia si usted hace su swizzling aquí. Si usted fuera a swizzle en +(void)initialize, sin embargo, usted podría terminar con una condición de carrera en su swizzling aplicación y el tiempo de ejecución podría terminar en un extraño estado.

Los cambios de comportamiento de las naciones unidas de propiedad del código

Este es un problema con swizzling, pero es de todo punto. El objetivo es ser capaz de cambiar ese código. La razón por la que la gente señalar esto como una gran cosa, es porque no estás solo cambiar las cosas para que la instancia de uno de los NSButton que quiere cambiar las cosas, sino para todos los NSButton casos en su aplicación. Por esta razón, usted debe ser cauteloso al momento de swizzle, pero no nee para evitar esto.

Piénsalo de esta manera... si se reemplaza un método en una clase y que no llame a la súper clase de método, puede causar problemas a surgir. En la mayoría de los casos, la super clase está a la espera de que el método para ser llamado (a menos que se especifique lo contrario). Si se aplica este mismo pensamiento a swizzling, que ha cubierto la mayoría de los problemas. Siempre llame a la implementación original. Si no, probablemente te estás cambiando mucho para estar seguro.

Posibles conflictos de nomenclatura

Los conflictos de nombres son un problema en todo el Cacao. Con frecuencia nos prefijo de los nombres de clase y los nombres de método en categorías. Desafortunadamente, los conflictos de nombres son una plaga en nuestro idioma. En el caso de swizzling, sin embargo, no tienen que ser. Solo tenemos que cambiar la forma en que pensamos sobre el método swizzling ligeramente. La mayoría de los swizzling se hace así:

@interface NSView : NSObject
- (void)setFrame:(NSRect)frame;
@end

@implementation NSView (MyViewAdditions)

- (void)my_setFrame:(NSRect)frame {
    // do custom work
    [self my_setFrame:frame];
}

+ (void)load {
    [self swizzle:@selector(setFrame:) with:@selector(my_setFrame:)];
}

@end

Esto funciona muy bien, pero ¿qué pasaría si my_setFrame: fue definido en otro lugar? Este problema no es exclusivo de swizzling, pero podemos trabajar alrededor de ella de todos modos. La solución tiene un beneficio adicional de hacer frente a otras dificultades, como bien. Esto es lo que hacen en lugar de:

@implementation NSView (MyViewAdditions)

static void MySetFrame(id self, SEL _cmd, NSRect frame);
static void (*SetFrameIMP)(id self, SEL _cmd, NSRect frame);

static void MySetFrame(id self, SEL _cmd, NSRect frame) {
    // do custom work
    SetFrameIMP(self, _cmd, frame);
}

+ (void)load {
    [self swizzle:@selector(setFrame:) with:(IMP)MySetFrame store:(IMP *)&SetFrameIMP];
}

@end

Si bien esto parece un poco menos como Objective-C (ya que es el uso de punteros de función), lo que evita cualquier tipo de conflictos de nombres. En principio, se trata de hacer exactamente lo mismo estándar swizzling. Esto puede ser un poco de un cambio para las personas que han estado usando swizzling como ha sido definido por un tiempo, pero en fin, yo creo que es mejor. El swizzling método se define así:

typedef IMP *IMPPointer;

BOOL class_swizzleMethodAndStore(Class class, SEL original, IMP replacement, IMPPointer store) {
    IMP imp = NULL;
    Method method = class_getInstanceMethod(class, original);
    if (method) {
        const char *type = method_getTypeEncoding(method);
        imp = class_replaceMethod(class, original, replacement, type);
        if (!imp) {
            imp = method_getImplementation(method);
        }
    }
    if (imp && store) { *store = imp; }
    return (imp != NULL);
}

@implementation NSObject (FRRuntimeAdditions)
+ (BOOL)swizzle:(SEL)original with:(IMP)replacement store:(IMPPointer)store {
    return class_swizzleMethodAndStore(self, original, replacement, store);
}
@end

Swizzling cambia el método de los argumentos de

Este es el más grande en mi mente. Esta es la razón por la que el método estándar swizzling no se debe hacer. Se están cambiando los argumentos que se pasan al método original de la aplicación. Aquí es donde sucede:

[self my_setFrame:frame];

Lo que hace es:

objc_msgSend(self, @selector(my_setFrame:), frame);

Que se utilice el tiempo de ejecución para buscar la aplicación de my_setFrame:. Una vez que la aplicación se encuentra, invoca la aplicación con los mismos argumentos que han dado. La aplicación se encuentra la implementación original de setFrame:, así que sigue adelante y llama a eso, pero el _cmd argumento no es setFrame: como debe ser. Ahora es my_setFrame:. La implementación original es llamado con un argumento que nunca esperaba recibir. Esto no es bueno.

Hay una solución simple de usar la alternativa swizzling técnica se define anteriormente. Los argumentos que se mantendrá sin cambios!

El orden de los asuntos swizzles

El orden en el que los métodos de obtener swizzled asuntos. Suponiendo setFrame: sólo está definida en NSView, imaginar este orden de cosas:

[NSButton swizzle:@selector(setFrame:) with:@selector(my_buttonSetFrame:)];
[NSControl swizzle:@selector(setFrame:) with:@selector(my_controlSetFrame:)];
[NSView swizzle:@selector(setFrame:) with:@selector(my_viewSetFrame:)];

¿Qué ocurre cuando el método en NSButton es swizzled? Bueno, la mayoría de swizzling se asegurará de que no es la sustitución de la aplicación de setFrame: para todas las vistas, para que tire hacia arriba el método de instancia. Esto va a hacer uso de la aplicación para re-definir setFrame: de la NSButton clase, de modo que el intercambio de implementaciones no afecta a todos los puntos de vista. La implementación existente es el que se define en NSView. Lo mismo sucederá cuando swizzling en NSControl (de nuevo utilizando el NSView de ejecución).

Cuando usted llame setFrame: sobre un botón, por lo tanto, llame a su swizzled método y, a continuación, saltar directamente a las setFrame: método definido originalmente en NSView. La NSControl y NSView swizzled implementaciones no se llama.

Pero, ¿y si la orden fueron:

[NSView swizzle:@selector(setFrame:) with:@selector(my_viewSetFrame:)];
[NSControl swizzle:@selector(setFrame:) with:@selector(my_controlSetFrame:)];
[NSButton swizzle:@selector(setFrame:) with:@selector(my_buttonSetFrame:)];

Desde el punto de vista swizzling se realiza en primer lugar, el control de swizzling será capaz de tirar el método correcto. Asimismo, desde el control de swizzling fue antes de que el botón swizzling, el botón de tirar el control del swizzled aplicación de setFrame:. Esto es un poco confuso, pero este es el orden correcto. ¿Cómo podemos garantizar que este orden de cosas?

De nuevo, sólo tiene que utilizar load a swizzle cosas. Si usted swizzle en load y sólo hacer cambios a la clase de ser cargado, usted estará a salvo. La load método garantiza que la super clase de carga método será llamado antes de cualquier subclases. Vamos a obtener el exacto orden correcto!

Difícil de entender (se ve recursiva)

Mirando un definido tradicionalmente swizzled método, creo que es muy difícil saber lo que está pasando. Pero mirando en la forma alternativa que hemos hecho swizzling anteriormente, es bastante fácil de entender. Este ya se ha resuelto!

Difícil de depurar

Una de las confusiones durante la depuración es ver una extraña traza donde el swizzled nombres se mezclan y todo se vuelve confuso en su cabeza. De nuevo, la alternativa de implementación de las direcciones de esta. Verás claramente las funciones con nombre en trazas. Aún así, swizzling puede ser difícil de depurar porque es difícil recordar cuál es el impacto de la swizzling está teniendo. Documentar el código de bien (incluso si crees que eres el único que va a ver). Siga buenas prácticas, y que va a estar bien. No es más difícil de depuración de multi-threaded código.

Conclusión

Método swizzling es seguro si se usa correctamente. Una simple medida de seguridad que usted puede tomar es sólo swizzle en load. Como muchas cosas en la programación, puede ser peligroso, pero la comprensión de las consecuencias que le permitirá utilizarlo correctamente.


1 el Uso de los definidos anteriormente, swizzling método, usted puede hacer que las cosas hilo de seguro si usted fuera a utilizar trampolines. Necesitaría dos trampolines. En el inicio del método, usted tiene que asignar la función de puntero, store, a una función que se movió hasta la dirección a la que store señaló cambiado. Esto evitaría cualquier tipo de condición de carrera en el que el swizzled método fue llamado antes de ser capaces de ajustar el store de la función de puntero. Entonces usted necesita utilizar un trampolín en el caso de que la aplicación no está ya definida en la clase y tienen el trampolín de búsqueda y llamar a la súper clase de método correctamente. Definir el método por lo que de forma dinámica busca la super aplicación se asegurará de que la orden de swizzling llamadas no importa.

11voto

Robert Puntos 10822

Primero voy a definir exactamente lo que quiero decir por el método swizzling:

  • Re-enrutamiento de todas las llamadas que originalmente fueron enviados a un método (llamado) a un nuevo método (llamado B).
  • Hemos propio Método B
  • Nosotros no tenemos un método propio de Un
  • Método B hace un poco de trabajo, a continuación, llama el método A.

Método swizzling es más general que este, pero este es el caso me interesa.

Peligros:

  • Cambios en la clase original. Nosotros no propia de la clase que estamos swizzling. Si los cambios de clase de nuestro swizzle puede dejar de trabajar.

  • Difícil de mantener. No sólo tienes que escribir y mantener el swizzled método. usted tiene que escribir y mantener el código que preformas el swizzle

  • Difíciles de depurar. Es difícil seguir el flujo de un swizzle, algunas personas no se dan cuenta de la swizzle ha sido realizado. Si hay errores introducidos desde el swizzle (quizás cuotas a los cambios en la clase original) que serán difíciles de resolver.

En resumen, usted debe mantener swizzling a un mínimo y considerar cómo los cambios en el original de la clase puedan afectar su swizzle. También debe claramente comentario y documentar lo que está haciendo (o simplemente eliminarlo por completo).

7voto

Caleb Puntos 72897

No es el swizzling sí que es muy peligroso. El problema es, como usted dice, que a menudo se usa para modificar el comportamiento de marco clases. Es suponiendo que usted sabe algo acerca de cómo esas clases privadas de trabajo que es "peligroso". Incluso si las modificaciones trabajo hoy en día, siempre hay una posibilidad de que Apple va a cambiar en el futuro y hacer que su modificación se rompa. También, si muchas aplicaciones diferentes de hacerlo, hace que sea mucho más difícil para Apple para cambiar el marco sin romper una gran cantidad de software existente.

5voto

Arafangion Puntos 5650

Utiliza con cuidado y prudencia, puede llevar a que el código elegante, pero por lo general, sólo confunde el código.

Yo digo que debe ser prohibido, a menos que sepa que presenta una forma muy elegante oportunidad para un determinado diseño de la tarea, pero necesitan saber claramente por qué se aplica bien a la situación, y por qué las alternativas no funcionan elegantemente para la situación.

Por ejemplo, una buena aplicación del método swizzling es isa swizzling, que es la forma en ObjC implementa el Valor de la Clave de Observación.

Un mal ejemplo podría ser el de confiar en el método swizzling como medio de ampliar sus clases, lo que conduce a altos niveles de acoplamiento.

5voto

user3288724 Puntos 31

Aunque he utilizado esta técnica, me gustaría señalar que:

  • Se ofusca su código, ya que puede causar onu-documentados, aunque deseado, efectos secundarios. Cuando uno lee el código que él/ella puede no ser conscientes de los efectos secundarios de comportamiento que se requiere a menos que él/ella recuerda a buscar en la base de código para ver si se ha swizzled. No estoy seguro de cómo aliviar este problema, porque no siempre es posible documentar cada lugar donde el código es dependiente de los efectos secundarios de swizzled comportamiento.
  • Esto puede hacer que tu código sea menos reutilizable, porque alguien que se encuentra un segmento de código que depende de la swizzled comportamiento que les gustaría para su uso en otros lugares no se puede simplemente cortar y pegar en alguna otra base de código, sin también la búsqueda y copia de la swizzled método.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X