chamada de método Intercept em Objective-C
-
06-07-2019 - |
Pergunta
Can I interceptar uma chamada de método em Objective-C? Como?
Editar: Mark Powell 's resposta me deu uma solução parcial, o -forwardInvocation método. Mas os estados de documentação que -forwardInvocation só é chamado quando um objeto é enviada uma mensagem para o qual ele não tem nenhum método correspondente. Eu gostaria de um método a ser chamado em todas as circunstâncias, mesmo se o receptor tem que selector.
Solução
Você faz isso por swizzling a chamada de método. Supondo que você quer pegar todos os lançamentos para 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@:");
Você pode obter mais detalhes em tempo de execução Objective C documentação .
Outras dicas
Interceptar chamadas de método em Objective-C (asuming é uma Objective-C, não uma chamada C) é feito com uma técnica chamada método swizzling.
Você pode encontrar uma introdução sobre como implementar essa aqui . Para um exemplo de como método swizzling é implementado em uma verificação de projeto real para fora OCMock (um isolamento Quadro de Objective-C).
O envio de uma mensagem em Objective-C é traduzido para uma chamada da função objc_msgSend(receiver, selector, arguments)
ou uma das suas variantes objc_msgSendSuper
, objc_msgSend_stret
, objc_msgSendSuper_stret
.
Se fosse possível alterar a implementação dessas funções, podemos interceptar qualquer mensagem. Infelizmente, objc_msgSend
faz parte do tempo de execução Objective-C e não pode ser substituído.
Por googling eu encontrei um artigo no Google Livros: a arquitetura reflexiva para o processo de Aplicações de Controle de Charlotte Pii Lunau . O documento apresenta um hack, redirecionando ponteiro classe isa
de um objeto para uma instância de uma classe MetaObject personalizado. As mensagens que foram destinados para o objecto modificado são, assim, enviados para o exemplo MetaObject. Como a classe MetaObject não tem métodos próprios, que pode então responder à invocação frente encaminhando a mensagem para o objeto modificado.
O documento não inclui os bits interessantes do código-fonte e eu não tenho idéia se tal abordagem teria efeitos colaterais em Cocoa. Mas pode ser interessante tentar.
Se você quiser registrar mensagem envia a partir do código do aplicativo, o -forwardingTargetForSelector: dica é parte da solução
.
Envolva seu objeto:
@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
Agora, fazer algo como isto:
Interceptor *i = [[[Interceptor alloc] init] autorelease];
NSFetchedResultsController *controller = [self setupFetchedResultsController];
i.interceptedTarget = controller;
controller = (NSFetchedResultsController *)i;
e você terá um registro de envio de mensagens. Note, envia enviado de dentro do objeto interceptada não será interceptado, já que será enviado com o objeto original 'self' ponteiro.
Se você quiser somente mensagens chamadas do exterior log (normalmente chamado de delegados; para ver qual tipo de mensagens, quando, etc.), você pode substituir respondsToSelector assim:
- (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;
}
Criar uma subclasse de NSProxy
e implementar -forwardInvocation:
e -methodSignatureForSelector:
(ou -forwardingTargetForSelector:
, se você 're simplesmente dirigi-lo a um segundo objeto em vez de mexer com o método você mesmo).
NSProxy
é uma classe projetada para implementar -forwardInvocation:
diante. Ele tem alguns métodos, mas na maior parte você não quer que eles sejam capturados. Por exemplo, a captura os métodos de contagem de referência impediria o proxy de ser desatribuído excepto sob a recolha de lixo. Mas se existem métodos específicos sobre NSProxy
que é absolutamente necessário para a frente, você pode substituir esse método especificamente e -forwardInvocation:
chamada manualmente. Note que simplesmente porque um método é listado sob a documentação NSProxy
não significa que implementa NSProxy
-lo, mas apenas que se espera que todos os objetos proxy tê-lo.
Se isto não vai funcionar para você, fornecer detalhes adicionais sobre sua situação.
Talvez você queira método NSObject
de -forwardInvocation
. Isso permite que você capture uma mensagem, redirecionar-lo e enviá-lo novamente.
Você pode swizzle a chamada de método com uma de sua preferência, que faz tudo o que você quer fazer em "interceptação" e chamadas através da implementação original. Swizzling é feito com class_replaceMethod ().
Uma chamada de método, não. A enviar mensagem, sim, mas você vai ter que ser muito mais descritivo, se você quiser uma boa resposta de como.
Para fazer alguma coisa quando um método é chamado, você pode tentar uma abordagem baseada em eventos. Então, quando o método é chamado, ele transmite um evento, que é captado por qualquer ouvinte. Eu não sou grande com C objetiva, mas eu só descobri algo semelhante usando NSNotificationCenter em Cocoa.
Mas se por "interceptar" quer dizer "stop", então talvez você precisa mais lógica para decidir wether o método deve ser chamado em tudo.