2009-07-06 11 views
46

Al utilizar categorías, puede reemplazar los métodos de aplicación con su propio modo:¿Anular un método vía Categoría ObjC y llamar a la implementación predeterminada?

// Base Class 
@interface ClassA : NSObject 
- (NSString *) myMethod; 
@end 
@implementation ClassA 
- (NSString*) myMethod { return @"A"; } 
@end 

//Category 
@interface ClassA (CategoryB) 
- (NSString *) myMethod; 
@end 
@implementation ClassA (CategoryB) 
- (NSString*) myMethod { return @"B"; } 
@end 

Al llamar al método "myMethod" después de incluir la categoría Nets el resultado "B".

¿Cuál es la forma más fácil para que la implementación de categoría de myMethod llame al Class A original myMethod? Tan cerca como pueda imaginar, tendrías que usar las llamadas de bajo nivel para obtener el método original de enganche para Clase A y llamar eso, pero parecía que habría una manera sintácticamente más fácil de hacer esto.

Respuesta

36

Si desea una forma hacker para hacer esto que implica limpiando con el objetivo-c tiempo de ejecución siempre se puede utilizar method swizzling (insertar renuncias estándar aquí.) se le permitirá para almacenar los diferentes métodos como selectores nombrados arbitrariamente, luego cambiarlos en el tiempo de ejecución cuando los necesite.

+10

No es necesariamente una forma "hackosa". El tiempo de ejecución de Objective-C está ahí por una razón. Es lo que hace que Objective-C sea superior a otros lenguajes compilados. –

+0

De acuerdo con mi experiencia, yendo al grano, quiere usar JRSwizzle, como se menciona en el enlace de arriba. Ha sido a prueba de balas para mí. –

+3

Enlace al código de JRSwizzle: https://github.com/rentzsch/jrswizzle ... Me encanta esto, el método swizzling es MUY poderoso siempre que quieras modificar una biblioteca de terceros para que haga lo que quieras. – LearnCocos2D

19

De comp.lang.objective-C FAQ listing:? ". ¿Qué pasa si múltiples categorías aplicar el mismo método A continuación, el tejido del universo tal como lo conocemos deja de existir En realidad, eso no es del todo cierto, pero ciertamente algunos problemas se producen cuando a. category implementa un método que ya apareció en una clase (ya sea a través de otra categoría o de la clase'implementación @ primaria '), la definición de esa categoría sobrescribe la definición que estaba presente anteriormente. La Objective-C ya no puede alcanzar la definición original. código. Tenga en cuenta que si dos categorías sobrescriben el mismo método, el que se haya cargado último "gana", lo que puede no ser posible predecir antes de que se inicie el código ".

De developer.apple.com: "Cuando una categoría anula un método heredado, el método en la categoría puede, como de costumbre, invocar la implementación heredada mediante un mensaje a super. Sin embargo, si una categoría prevalece sobre un método que ya existía en la categoría clase, no hay manera de invocar la implementación original"

+0

Sí, esto realmente no es compatible con el idioma. – Chuck

+0

Gracias por el comentario, pero estoy buscando cómo se puede hacer (porque sé que puede ser, simplemente no es fácil) no cómo es imposible ... –

+0

Creo que 'super' debería resolverse en el objeto original. Después de todo, una categoría es muy parecida a una subclase (por ejemplo, puede anular los métodos). –

12

Compruebe hacia fuera mi artículo acerca de una solución que se encontró en el Mac Developer Library: http://codeshaker.blogspot.com/2012/01/calling-original-overridden-method-from.html

Básicamente, es el mismo que el anterior Método Swizzling con un breve ejemplo:

 
#import <objc/runtime.h> 

@implementation Test (Logging) 

- (NSUInteger)logLength { 
    NSUInteger length = [self logLength]; 
    NSLog(@"Logging: %d", length); 
    return length; 
} 

+ (void)load { 
    method_exchangeImplementations(class_getInstanceMethod(self, @selector(length)), class_getInstanceMethod(self, @selector(logLength))); 
} 

@end 
1

Con la swizzling " ayuda "incluidos en ConciseKit, realmente llama a la implementación predeterminada ... lo suficientemente extraño .. llamando a su implementación SWIZZLED ..

Se configura en + (void) load, llamando + (BOOL)swizzleMethod:(SEL)originalSelector with:(SEL)anotherSelector in:(Class)klass;, es decir

[$ swizzleMethod:@selector(oldTired:) 
      with:@selector(swizzledHotness:) in:self.class]; 

y luego en el método swizzled .. vamos a suponer que vuelve -(id) .. usted puede hacer su travesura, o lo que sea razón por la que se Swizzling en el primer coloque ... y entonces, en lugar de devolver un objeto, o self, o lo que sea ..

return [self swizzledHotness:yourSwizzledMethodsArgument]; 

As explained here…

En este método, parece que volvemos a llamar al mismo método, lo que provoca recursiones sin fin. Pero cuando se alcanza esta línea, los dos métodos han sido intercambiados. Entonces, cuando llamamos a swizzled_synchronize, estamos llamando al método original.

Se siente y se ve extraño, pero ... funciona. Esto le permite agregar infinitos embellecimientos a los métodos existentes, y aún así "llamar al súper" (realmente uno mismo) y cosechar los beneficios de la obra original del método ... incluso sin acceso a la fuente original.

Cuestiones relacionadas