Come si implementa il metodo Swizzling?
-
27-10-2019 - |
Domanda
Sto cercando di modificare il comportamento di un programma (non ho la sua fonte) usando SIMBL. Ho usato il dump di classe e ho scoperto che ho bisogno di un metodo di istanza
Questo metodo è nella classe chiamata controller. Tutto quello che devo fare è ottenere l'argomento arg1 e questo è tutto. Forse NSLOG IT o POST di una notifica ... Ho letto del metodo Swizzling in Objective-C ma come posso usarlo?. Avrei bisogno di fare riferimento al MessageController di classe il cui corso non ho.
Grazie!
Soluzione
Immagino che tu debba chiamare l'implementazione originale dopo aver fatto il tuo NSLog; In caso contrario, potresti essere in grado di utilizzare solo una categoria sulla classe per sovrascrivere il metodo.
Per ridotto il metodo, prima è necessario un metodo di sostituzione. Di solito metto qualcosa di simile in una categoria nella classe target:
- (void)replacementReceiveMessage:(const struct BInstantMessage *)arg1 {
NSLog(@"arg1 is %@", arg1);
[self replacementReceiveMessage:arg1];
}
Sembra che si chiamerà ricorsivamente, ma non lo farà perché scambieremo le cose, quindi chiameremo ReceiveMessage:
chiamate questo metodo mentre chiama replacementReceiveMessage:
chiama la vecchia versione.
Il secondo passo è utilizzare le funzioni di runtime per eseguire effettivamente lo scambio. Il vantaggio di utilizzare una categoria è che puoi usare load
Nella categoria per fare il lavoro:
+ (void)load {
SEL originalSelector = @selector(ReceiveMessage:);
SEL overrideSelector = @selector(replacementReceiveMessage:);
Method originalMethod = class_getInstanceMethod(self, originalSelector);
Method overrideMethod = class_getInstanceMethod(self, overrideSelector);
if (class_addMethod(self, originalSelector, method_getImplementation(overrideMethod), method_getTypeEncoding(overrideMethod))) {
class_replaceMethod(self, overrideSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, overrideMethod);
}
}
Ci sono due casi che devono essere gestiti:
- Se il metodo che stiamo swizzling è effettivamente definito in una superclasse, dobbiamo usare
class_addMethod
Per aggiungere un'implementazione diReceiveMessage:
alla classe target, che facciamo utilizzando la nostra implementazione di sostituzione. Quindi possiamo usareclass_replaceMethod
RimpiazzarereplacementReceiveMessage:
Con l'implementazione della Superclass, quindi la nostra nuova versione sarà in grado di chiamare correttamente il vecchio. - Se il metodo è definito nella classe target,
class_addMethod
fallirà ma poi possiamo usaremethod_exchangeImplementations
Per scambiare le versioni nuove e vecchie.
Altri suggerimenti
La Biblioteca jrsswizzle gestiscelo. Farlo da solo non è consigliato, perché ci sono molti dettagli per ottenere bene. (Vedi la tabella che documenta i guasti delle precedenti implementazioni in JRSWizzle Readme.)
Supponi di avere una classe come questa:
@interface Weh : NSObject
-(void)foo;
-(void)bar;
@end
@implementation Weh
-(void)foo {
NSLog(@"Foo called");
}
-(void)bar {
NSLog(@"Bar called");
[self bar];
}
@end
Puoi usarlo in questo modo:
Weh *weh = Weh.new;
[weh foo];
[Weh jr_swizzleMethod:@selector(foo) withMethod:@selector(bar) error:nil];
[weh foo];
Produzione:
Foo called
Bar called
Foo called