2013-04-09 2 views
8

È possibile trovare il punto in cui in una gerarchia di classi proviene il metodo recuperato da class_getInstanceMethod? Ad esempio, supponiamo che la classe A implementa myMethod. Ora dì che ho sottoclasse la Classe A in Classe A1. Se chiamo class_getInstanceMethod(ClassA1, myMethod), è possibile sapere se il metodo risultante è stato sovrascritto in ClassA1 o proviene direttamente da A1?Utilizzo di class_getInstanceMethod - dove è implementato il metodo nella gerarchia di classi?

suppongo che sarebbe stato possibile confrontare gli indirizzi di memoria dei PIM se si ha accesso sia ClassA e ClassA1, ma non hanno accesso diretto ad A.

risposta

15

È sempre possibile accedere a una classe superclasse, quindi puoi passarlo a class_getInstanceMethod o class_getMethodImplementation con lo stesso SEL e confrontare gli indirizzi IMP per vedere se il metodo è stato sovrascritto dalla sottoclasse.

Questo diventa un po 'più impegnativo se si desidera ottenere la classe radice che definisce questo metodo.

In ogni caso, ecco qui:

static inline BOOL isInstanceMethodOverridden(Class cls, SEL selector, Class *rootImpClass) { 
    IMP selfMethod = class_getMethodImplementation(cls, selector); 
    BOOL overridden = NO; 
    Class superclass = [cls superclass]; 
    while(superclass && [superclass superclass]) { 
     IMP superMethod = class_getMethodImplementation(superclass, selector); 
     if(superMethod && superMethod != selfMethod) { 
      overridden = YES; 
      if(!rootImpClass) { 
       //No need to continue walking hierarchy 
       break; 
      } 
     } 

     if(!superMethod && [cls respondsToSelector:selector])) { 
      //We're at the root class for this method 
      if(rootImpClass) *rootImpClass = cls; 
      break; 
     } 

     cls = superclass; 
     superclass = [cls superclass]; 
    } 

    return overridden; 
} 
+2

questo è impressionante –

+0

Questo non riuscirà per gli oggetti che non sottoclasse 'NSObject', come ad esempio' NSProxy'. Solo una nota. –

+0

@ RichardJ.RossIII Vedi aggiornamento. :) –

3

Ecco la mia versione modificata del codice di Giacobbe. Ho avuto problemi con il suo perché class_getMethodImplementation stava restituendo un _objc_msgForward che stava causando il trattamento dei metodi come sovrascritto. Anche io non ho bisogno di * rootImpClass ma è abbastanza semplice per aggiungere di nuovo in.

inline BOOL isInstanceMethodOverridden(Class cls, SEL selector) 
{ 
    IMP selfImplementation = class_getMethodImplementation(cls, selector); 

    BOOL overridden = NO; 
    Class superclass = cls; 

    while ((superclass = [superclass superclass])) 
    { 
     Method superMethod = class_getInstanceMethod(superclass, selector); 
     if (superMethod && method_getImplementation(superMethod) != selfImplementation) 
     { 
      overridden = YES; 
      break; 
     } 
    } 

    return overridden; 
}