2009-07-06 9 views
46

Quando si utilizza categorie, è possibile sostituire i metodi di implementazione con il proprio in questo modo:Sostituire un metodo tramite Categoria ObjC e chiamare l'implementazione predefinita?

// 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 

chiamata al metodo "myMethod" dopo aver incluso la categoria le reti con risultato "B".

Qual è il modo più semplice per l'implementazione di categoria di myMethod per chiamare la classe originale MyMethod? Per quanto riesca a capire, dovresti usare le chiamate di basso livello per ottenere il metodo originale di aggancio per la Classe A e chiamarlo, ma sembrava che ci sarebbe stato un modo sintatticamente più semplice per farlo.

risposta

36

Se si desidera un modo hacking per eseguire questa operazione che coinvolge il roking con il runtime oggettivo, è sempre possibile utilizzare method swizzling (inserire dichiarazioni di non responsabilità standard qui). per memorizzare i diversi metodi come selettori con nome arbitrario, quindi scambiarli in fase di esecuzione quando ne hai bisogno.

+10

Non è necessariamente un modo 'hacker'. Il runtime Objective-C è lì per un motivo. È ciò che rende Objective-C superiore ad altri linguaggi compilati. –

+0

Sulla base della mia esperienza, tagliando all'inseguimento, si desidera utilizzare JRSwizzle, come indicato nel link precedente. È stato a prova di proiettile per me. –

+3

Link al codice JRSwizzle: https://github.com/rentzsch/jrswizzle ... Sono arrivato ad amare questo metodo, il swizzling è MOLTO potente ogni volta che vuoi modificare una libreria di terze parti per fare come ti pare! – LearnCocos2D

19

Da comp.lang.objective-C FAQ listing:? ". E se più categorie implementano lo stesso metodo Poi il tessuto dell'universo come lo conosciamo cesserà di esistere In realtà, che non è del tutto vero, ma certamente qualche problema sarà causato Quando una. la categoria implementa un metodo che è già apparso in una classe (sia attraverso un'altra categoria, sia con la classe 'primary @implementation), la definizione di quella categoria sovrascrive la definizione che era precedentemente presente.La definizione originale non può più essere raggiunta dall'Obiettivo-C codice. Si noti che se due categorie sovrascrivono lo stesso metodo, a seconda di quale sia stato caricato l'ultimo "wins", potrebbe non essere possibile prevedere prima che il codice venga avviato. "

Da developer.apple.com: "Quando una categoria sovrascrive un metodo ereditato, il metodo nella categoria può, come al solito, richiamare l'implementazione ereditata tramite un messaggio a super.Tuttavia, se una categoria sostituisce un metodo già esistente nella categoria classe, non c'è modo di invocare l'implementazione originale "

+0

Sì, questo in realtà non è supportato dalla lingua. – Chuck

+0

Ti ringrazio per il commento, ma sto cercando come può essere fatto (perché so che può essere, ma non facilmente) non come sia impossibile ... –

+0

Penso che 'super' debba risolversi oggetto originale. Dopo tutto, una categoria è molto simile a una sottoclasse (ad esempio, è possibile sovrascrivere i metodi). –

12

controllare il mio articolo su una soluzione trovata sul Developer Library Mac: http://codeshaker.blogspot.com/2012/01/calling-original-overridden-method-from.html

In sostanza, è la stessa come il metodo di cui sopra Swizzling con un breve esempio:

 
#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 lo swizzling " helper "incluso in ConciseKit, in realtà si chiama l'implementazione predefinita ... stranamente .. chiamando l'implementazione SWIZZLED ..

lo si imposta in + (void) load, chiamando + (BOOL)swizzleMethod:(SEL)originalSelector with:(SEL)anotherSelector in:(Class)klass;, cioè

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

e poi nel metodo swizzled .. supponiamo restituisce -(id) .. si può fare il vostro male, o qualsiasi motivo si sono swizzling nel primo posto ... e poi, invece di restituire un oggetto, o self, o roba del genere ..

return [self swizzledHotness:yourSwizzledMethodsArgument]; 

As explained here…

In questo metodo, sembra che stiamo chiamando di nuovo lo stesso metodo, causando una ricorsione senza fine. Ma nel momento in cui questa linea viene raggiunta, i due metodi sono stati scambiati. Quindi, quando chiamiamo swizzled_synchronize, stiamo effettivamente chiamando il metodo originale.

Sembra strano, ma ... funziona. Ciò ti consente di aggiungere infiniti abbellimenti ai metodi esistenti, e comunque di "chiamare super" (in realtà te stesso) e di cogliere i benefici del lavoro manuale del metodo originale ... anche senza accesso alla fonte originale.

Problemi correlati