¿Usando Super en una Categoría de Objetivo C?
-
05-07-2019 - |
Pregunta
Me gustaría anular un método en una clase de Objective C que no tengo la fuente.
Lo he investigado y parece que las Categorías deberían permitirme hacer esto, pero me gustaría usar el resultado del método antiguo en mi nuevo método, usando super para obtener el resultado de los métodos antiguos.
Sin embargo, siempre que intento esto, se llama a mi método, pero " super " es nil ... ¿Alguna idea de por qué? Estoy haciendo el desarrollo de iPhone con XCode 2.2 SDK. Definitivamente estoy trabajando con una instancia de una clase, y el método de la clase es un método de instancia.
@implementation SampleClass (filePathResolver)
-(NSString*) fullPathFromRelativePath:(NSString*) relPath
{
NSString *result = [super fullPathFromRelativePath: relPath];
... do some stuff with the old result
return result;
}
Nota y aclaración: por lo que puedo ver en los Apple Docs, me parece que esto debería estar permitido.
Documentos de categorías en developer.apple.com: Cuando una categoría anula un método heredado, el método en el La categoría puede, como es habitual, invocar el implementación heredada a través de un mensaje Super. Sin embargo, si una categoría anula un método que ya existía en la categoría de la categoría, allí No hay manera de invocar el original. implementación.
Solución
Las categorías extienden la clase original, pero no las subclasifican, por lo tanto, una llamada a super
no encuentra el método.
Lo que quieres se llama Método Swizzling . Pero ten en cuenta que tu código podría romper algo. Hay un artículo en Theocacao escrito por Scot Stevenson sobre el Método Swizzling en el antiguo Runtime de Objective-C, Cocoa with Love por Matt Gallagher tiene un artículo sobre el método Swizzling en el nuevo objetivo -C 2.0 tiempo de ejecución y un simple reemplazo para él.
Como alternativa, puede crear una subclase de la clase y luego usar la subclase o usar + (void) poseAsClass: (Class) aClass
para reemplazar la superclase . Apple escribe:
Un método definido por una clase posando Puede, a través de un mensaje a
super
, Incorporar el método superclase. anulaciones.
Tenga en cuenta que Apple ha desaprobado poseAsClass:
en Mac OS X 10.5.
Otros consejos
Si va a codificar contra esta clase, simplemente cambie el nombre del selector a algo que su código pueda usar, y llame al selector original en self
:
@implementation SampleClass (filePathResolver)
-(NSString*) myFullPathFromRelativePath:(NSString*) relPath
{
NSString *result = [self fullPathFromRelativePath: relPath];
... do some stuff with the old result
return result;
}
Si desea anular la implementación predeterminada de este selector para esa clase, deberá usar método swizzling enfoque.
No exactamente en la categoría pero hay una solución al agregar el método dinámicamente en tiempo de ejecución. Samuel Défago en su artículo describe una forma clara de crear una implementación IMP de bloque llamando a super, su artículo original se puede encontrar aquí
El código relevante es:
#import <objc/runtime.h>
#import <objc/message.h>
const char *types = method_getTypeEncoding(class_getInstanceMethod(clazz, selector));
class_addMethod(clazz, selector, imp_implementationWithBlock(^(__unsafe_unretained id self, va_list argp) {
struct objc_super super = {
.receiver = self,
.super_class = class_getSuperclass(clazz)
};
id (*objc_msgSendSuper_typed)(struct objc_super *, SEL, va_list) = (void *)&objc_msgSendSuper;
return objc_msgSendSuper_typed(&super, selector, argp);
}), types);