2011-05-06 7 views
7

Estoy tratando de escribir un código dinámico donde un usuario puede intentar llamar a un método desde una instancia específica de una clase y hacer que se resuelva en tiempo de ejecución. La implementación para recuperar la información existe, pero el método para acceder no es porque está en cada instancia.¿Puede class_addMethod en Objective-C trabajar solo en una instancia específica?

Por ejemplo, un usuario puede querer llamar a un método llamado "getSomething", que no existe en la clase:

[someInstance getSomething] 

En esta situación, yo quiero tener una aplicación resuelta que tiene una variable tipo de devolución que solo se aplicará a la instancia en la que se está trabajando. Estaba considerando usar class_addMethod del Objective-C, pero no estoy 100% seguro de su comportamiento. En la documentación, afirma que esto se puede usar para agregar métodos de clase o instancia. ¿Llamar a esta clase agrega el método solo a la instancia específica oa la clase para que cada instancia creada posteriormente tenga el método en ella? También leí que una vez que se agrega un método, no puede eliminarlo.

Tal vez mi enfoque no es correcto, por lo que si se conocen alternativas, lo agradecería. No puedo usar el reenvío de mensajes porque no hay una clase que entienda el selector ya implementado.

Respuesta

0

No estoy familiarizado con class_addMethod, pero tal vez esto puede ayudar a aclarar para usted:

Hay que recordar que en Objective-C no está "llamando a un método", pero en realidad se está enviando un mensaje. Entonces es seguro hacer: [anyObject anyMethodName] en cualquier objeto instanciado. Este objeto puede o no responder al mensaje.

Puede comprobar si un objeto estará o no usando [anyObject respondeStoSelector: @selector (@ "anyMethodName")] marca, y si eso es SÍ, entonces continúe y realice la llamada [anyObject anyMethodName]. No puedo entender completamente la descripción de su problema, pero parece que tiene un contenedor heterogéneo lleno de objetos que pueden o no responder a la llamada. Hacer este "responder al selector:" controlar cada objeto en el contenedor es algo totalmente normal en Objective-C y suena como buen diseño

Si cada objeto devuelve un tipo de datos diferente, puede manejarlo usando el 'id' tipo genérico. Es decir, id returnData = [anyObject anyMethodName]; Entonces, puede usar la introspección en el returnData, o puede manejar las cosas de manera diferente en función de la clase de 'anyObject', comprobada por [anyObject class];

Así como,

if([anyObject class] == MyGreatClass) // recast data to MyGreatClassCoolReturnType

espero que esto ayuda a responder a la pregunta

+0

Gracias por tomarse el tiempo para responder a mi pregunta. Esto no es realmente lo que estaba buscando, pero sí me da una idea de otras áreas sobre las que no estaba claro. – gtaborga

2

class_addMethod() añade un método de instancia a un objeto de clase o un método de clase a un objeto metaclase. En otras palabras, nunca puede agregar un método solo a una instancia de una clase.

En cambio, si realmente necesita este comportamiento, puede implementar -forwardInvocation:, donde el objeto receptor puede decidir si tiene suficiente información para completar el mensaje. Tenga en cuenta que una implementación de -forwardInvocation: generalmente requiere la implementación de -methodSignatureForSelector: y -respondsToSelector: también.

7

Otra forma en que podría hacerlo es con una subclase dinámica:

- (void)addCustomMethodToObject:(id)object { 
    Class objectClass = object_getClass(object); 
    SEL selectorToOverride = ...; // this is the method name you want to override 

    NSString *newClassName = [NSString stringWithFormat:@"Custom_%@", NSStringFromClass(objectClass)]; 
    Class c = NSClassFromString(newClassName); 
    if (c == nil) { 
    // this class doesn't exist; create it 
    // allocate a new class 
    c = objc_allocateClassPair(objectClass, [newClassName UTF8String], 0); 
    // get the info on the method we're going to override 
    Method m = class_getInstanceMethod(objectClass, selectorToOverride); 
    // add the method to the new class 
    class_addMethod(c, selectorToOverride, (IMP)myCustomFunction, method_getTypeEncoding(m)); 
    // register the new class with the runtime 
    objc_registerClassPair(c); 
    } 
    // change the class of the object 
    object_setClass(object, c); 
} 

id myCustomFunction(id self, SEL _cmd, [other params...]) { 
    // this is the body of the instance-specific method 
    // you may call super to invoke the original implementation 
} 

Después de hacer esto, solamente object habrán recibido el método reemplazado, ya que será la única cosa que es una instancia de la clase especial . Además, este código solo anula los métodos de instancia, pero no sería difícil modificarlo para anular los métodos de clase.

Como siempre, las advertencias habituales:

  1. Advertencia Implementor: este código se escribe en un navegador
  2. Advertencia Observador: este código no juega bien con la clave-valor de la observación de
  3. Caveat Threader: este código no parece muy seguro para subprocesos
  4. Caveat ARC'er: objc_allocateClassPair() no se puede compilar con ARC.
  5. Desarrollador de advertencias: perder el tiempo con la clase de un objeto es algo peligroso. Existen usos perfectamente legítimos para este tipo de vudú, pero son muy raros. Si cree que necesita hacer esto, probablemente esté equivocado, y debería publicar una nueva pregunta aquí diciendo: "esto es lo que creo que debo hacer, ¿o sí?"
+1

También hay un poco de intercambio de tiempo/espacio involucrado aquí. Si cada instancia admite diferentes métodos, la cantidad de clases dinámicas en el tiempo de ejecución podría volverse difícil de manejar. Por el contrario, si realmente hay un solo método que algunos tienen y otros no, el reenvío de métodos puede tener un costo de velocidad inaceptable, y una subclase dinámica podría ser mucho más rápida. –

Cuestiones relacionadas