Domanda

Posso intercettare una chiamata di metodo in Objective-C? Come?

Modifica La risposta di Mark Powell mi ha dato una soluzione parziale, il metodo -forwardInvocation . Ma la documentazione afferma che -forwardInvocation viene chiamato solo quando un oggetto viene inviato un messaggio per il quale non ha un metodo corrispondente. Vorrei che un metodo fosse chiamato in ogni circostanza, anche se il ricevitore ha quel selettore.

È stato utile?

Soluzione

Lo fai facendo scorrere la chiamata del metodo. Supponendo di voler prendere tutte le versioni di NSTableView:

static IMP gOriginalRelease = nil;
static void newNSTableViewRelease(id self, SEL releaseSelector, ...) {
   NSLog(@"Release called on an NSTableView");
   gOriginalRelease(self, releaseSelector);
}


  //Then somewhere do this:
  gOriginalRelease = class_replaceMethod([NSTableView class], @selector(release), newNSTableViewRelease, "v@:");

Puoi ottenere maggiori dettagli nel runtime di Objective documentazione .

Altri suggerimenti

L'intercettazione delle chiamate del metodo in Objective-C (supponendo che sia una Objective-C, non una chiamata C) viene eseguita con una tecnica chiamata metodo che sfrigola.

Puoi trovare un'introduzione su come implementare qui . Per un esempio di come viene implementato il metodo swizzling in un progetto reale, consulta OCMock (un Quadro di isolamento per Objective-C).

L'invio di un messaggio in Objective-C viene tradotto in una chiamata della funzione objc_msgSend (destinatario, selettore, argomenti) o in una delle sue varianti objc_msgSendSuper , objc_msgSend_stret , objc_msgSendSuper_stret .

Se fosse possibile modificare l'implementazione di queste funzioni, potremmo intercettare qualsiasi messaggio. Sfortunatamente, objc_msgSend fa parte del runtime di Objective-C e non può essere ignorato.

Cercando su Google ho trovato un documento su Google Libri: Un'architettura riflettente per le applicazioni di controllo dei processi di Charlotte Pii Lunau . L'articolo introduce un hack reindirizzando il puntatore di classe isa di un oggetto a un'istanza di una classe MetaObject personalizzata. I messaggi destinati all'oggetto modificato vengono quindi inviati all'istanza MetaObject. Poiché la classe MetaObject non ha metodi propri, può quindi rispondere all'invocazione diretta inoltrando il messaggio all'oggetto modificato.

Il documento non include i pezzi interessanti del codice sorgente e non ho idea se un tale approccio avrebbe effetti collaterali in Cocoa. Ma potrebbe essere interessante provare.

Se si desidera registrare i messaggi inviati dal codice dell'applicazione, il -forwardingTargetForSelector: tip fa parte della soluzione.
Avvolgi il tuo oggetto:

@interface Interceptor : NSObject
@property (nonatomic, retain) id interceptedTarget;
@end

@implementation Interceptor
@synthesize interceptedTarget=_interceptedTarget;

- (void)dealloc {
    [_interceptedTarget release];
    [super dealloc];
}

- (id)forwardingTargetForSelector:(SEL)aSelector {
    NSLog(@"Intercepting %@", NSStringFromSelector(aSelector));
    return self.interceptedTarget;
} 

@end

Ora fai qualcosa del genere:

Interceptor *i = [[[Interceptor alloc] init] autorelease];
NSFetchedResultsController *controller = [self setupFetchedResultsController];
i.interceptedTarget = controller;
controller = (NSFetchedResultsController *)i;

e avrai un registro degli invii dei messaggi. Nota, gli invii inviati dall'interno dell'oggetto intercettato non verranno intercettati, in quanto verranno inviati utilizzando il puntatore "self" dell'oggetto originale.

Se vuoi solo registrare i messaggi chiamati dall'esterno (di solito chiamati dai delegati; per vedere quale tipo di messaggi, quando, ecc.), puoi ignorare le risposte a SelSelector in questo modo:

- (BOOL)respondsToSelector:(SEL)aSelector {
NSLog(@"respondsToSelector called for '%@'", NSStringFromSelector(aSelector));

// look up, if a method is implemented
if([[self class] instancesRespondToSelector:aSelector]) return YES;

return NO;

}

Crea una sottoclasse di NSProxy e implementa -forwardInvocation: e -methodSignatureForSelector: (o -forwardingTargetFor Selettore: , se lo stai semplicemente dirigendo verso un secondo oggetto invece di armeggiare con il metodo tu stesso.

NSProxy è una classe progettata per implementare -forwardInvocation: su. Ha alcuni metodi, ma soprattutto non li vuoi catturare. Ad esempio, la cattura dei metodi di conteggio dei riferimenti impedirebbe la deallocazione del proxy, tranne durante la garbage collection. Ma se ci sono metodi specifici su NSProxy che devi assolutamente inoltrare, puoi sovrascrivere quel metodo in modo specifico e chiamare -forwardInvocation: manualmente. Si noti che semplicemente perché un metodo è elencato nella documentazione NSProxy non significa che NSProxy lo implementa, ma semplicemente è previsto che tutti gli oggetti proxy lo abbiano.

Se questo non funziona per te, fornisci ulteriori dettagli sulla tua situazione.

Forse vuoi il metodo di NSObject -forwardInvocation . Ciò ti consente di catturare un messaggio, eseguirne il retarget e quindi inviarlo di nuovo.

Puoi far scorrere la chiamata del metodo con una tua, che fa quello che vuoi fare su " intercettazione " e chiama all'implementazione originale. Lo swizzling viene eseguito con class_replaceMethod ().

Una chiamata di metodo, no. Invia un messaggio, sì, ma dovrai essere molto più descrittivo se vuoi una buona risposta su come.

Per fare qualcosa quando viene chiamato un metodo, puoi provare un approccio basato sugli eventi. Quindi, quando viene chiamato il metodo, trasmette un evento, che viene raccolto da tutti gli ascoltatori. Non sono bravo con l'obiettivo C, ma ho appena capito qualcosa di simile usando NSNotificationCenter in Cocoa.

Ma se per " intercetta " intendi "stop", quindi forse hai bisogno di più logica per decidere se il metodo dovrebbe essere chiamato affatto.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top