objective-c ios methods categories swizzling

objective c - Cambiando una sola instancia, no una clase



objective-c ios (3)

Tengo una categoría en NSObject que se supone que algunas cosas. Cuando lo llamo a un objeto, me gustaría anular su método dealloc para hacer algunas limpiezas.

Quería hacerlo usando el método swizzling, pero no podía entender cómo. Los únicos ejemplos que he encontrado son sobre cómo reemplazar la implementación del método para toda la clase (en mi caso, reemplazaría a dealloc para TODOS los NSObjects, lo cual no quiero).

Quiero anular el método dealloc de instancias específicas de NSObject.

@interface NSObject(MyCategory) -(void)test; @end @implementation NSObject(MyCategory) -(void)newDealloc { // do some cleanup here [self dealloc]; // call actual dealloc method } -(void)test { IMP orig=[self methodForSelector:@selector(dealloc)]; IMP repl=[self methodForSelector:@selector(newDealloc)]; if (...) // ''test'' might be called several times, this replacement should happen only on the first call { method_exchangeImplementations(..., ...); } } @end


Hice una API swizzling que también incluye swizzling específicos de la instancia. Creo que esto es exactamente lo que estás buscando: https://github.com/JonasGessner/JGMethodSwizzler

Funciona mediante la creación de una subclase dinámica para la instancia específica que está haciendo en el tiempo de ejecución.


La selección del método se basa en la clase de una instancia de objeto, por lo que el método swizzling afecta a todas las instancias de la misma clase, como descubrió.

Pero puedes cambiar la clase de una instancia, ¡pero debes tener cuidado! Aquí está el esquema, suponga que tiene una clase:

@instance MyPlainObject : NSObject - (void) doSomething; @end

Ahora, si solo para algunas de las instancias de MyPlainObject desea modificar el comportamiento de doSomething primero debe definir una subclase:

@instance MyFancyObject: MyPlainObject - (void) doSomething; @end

Ahora puede hacer claramente instancias de MyFancyObject , pero lo que necesitamos hacer es tomar una instancia preexistente de MyPlainObject y convertirla en un MyFancyObject para obtener el nuevo comportamiento. Para eso podemos cambiar la clase, agregue lo siguiente a MyFancyObject :

static Class myPlainObjectClass; static Class myFancyObjectClass; + (void)initialize { myPlainObjectClass = objc_getClass("MyPlainObject"); myFancyObjectClass = objc_getClass("MyFancyObject"); } + (void)changeKind:(MyPlainObject *)control fancy:(BOOL)fancy { object_setClass(control, fancy ? myFancyObjectClass : myPlainObjectClass); }

Ahora, para cualquier instancia original de MyPlainClass , puede cambiar para comportarse como MyFancyClass , y viceversa:

MyPlainClass *mpc = [MyPlainClass new]; ... // masquerade as MyFancyClass [MyFancyClass changeKind:mpc fancy:YES] ... // mpc behaves as a MyFancyClass // revert to true nature [MyFancyClass changeKind:mpc: fancy:NO];

(Algunas) de las advertencias:

Solo puede hacer esto si la subclase anula o agrega métodos, y agrega variables static (clase).

También necesita una subclase para cada clase en la que desee cambiar el comportamiento, no puede tener una sola clase que pueda cambiar el comportamiento de muchas clases diferentes.


Realmente no puedes hacer esto ya que los objetos no tienen sus propias tablas de métodos. Solo las clases tienen tablas de métodos y si las cambias afectará a todos los objetos de esa clase. Sin embargo, hay una manera directa de evitar esto: cambiar la clase de su objeto en tiempo de ejecución a una subclase creada dinámicamente. Apple utiliza esta técnica, también llamada isa-swizzling, para implementar el KVO automático.

Este es un método poderoso y tiene sus usos. Pero para su caso hay un método más fácil usando objetos asociados. Básicamente, utiliza objc_setAssociatedObject para asociar otro objeto a su primer objeto que realiza la limpieza en su dealloc . Puedes encontrar más detalles en esta entrada de blog en Cocoa is my Girlfriend .