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.

Foi útil?

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.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top