Question

Puis-je intercepter un appel de méthode dans Objective-C? Comment?

Modifier: La réponse de Mark Powell m’a donné une solution partielle, la méthode -forwardInvocation . Mais la documentation indique que -forwardInvocation est appelé uniquement lorsqu'un objet reçoit un message pour lequel il n'a pas de méthode correspondante. J'aimerais qu'une méthode soit appelée dans toutes les circonstances, même si le récepteur dispose de ce sélecteur.

Était-ce utile?

La solution

Vous le faites en balayant l’appel de la méthode. En supposant que vous souhaitiez récupérer toutes les versions de 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@:");

Vous pouvez obtenir plus de détails dans le runtime d'Objective C documentation .

Autres conseils

L’interception des appels de méthode dans Objective-C (s’il s’agit d’un appel Objective-C, et non d’un appel C) est effectuée à l’aide d’une technique appelée méthode swizzling.

Vous pouvez trouver une introduction sur la manière de mettre en œuvre cette ici . Pour un exemple de mise en œuvre de la méthode swizzling dans un projet réel, consultez OCMock (une Cadre d’isolation pour Objective-C).

L'envoi d'un message en Objective-C est traduit en appel de la fonction objc_msgSend (destinataire, sélecteur, arguments) ou de l'une de ses variantes objc_msgSendSuper , objc_msgSend_stret , objc_msgSendSuper_stret .

S'il était possible de changer la mise en œuvre de ces fonctions, nous pourrions intercepter n'importe quel message. Malheureusement, objc_msgSend fait partie de l'exécution d'Objective-C et ne peut pas être remplacé.

p -ALnSozvMceG_Abn39SCCA & sa = X & oi = book_result & ct = result & resnum = 1 & ved = 0CAgQ6AEwAA # v = onepage & q = & f = false "rel =" noreferrer "> Architecture de réflexion pour les applications de contrôle de processus de Charlotte Pii Lunau . Le document introduit un hack en redirigeant le pointeur de la classe isa de l'objet vers une instance d'une classe MetaObject personnalisée. Les messages destinés à l'objet modifié sont donc envoyés à l'instance MetaObject. Étant donné que la classe MetaObject n'a pas de méthode propre, elle peut alors répondre à l'invocation de transfert en transmettant le message à l'objet modifié.

Le document n'inclut pas les éléments intéressants du code source et je ne sais pas si une telle approche aurait des effets secondaires dans Cocoa. Mais il pourrait être intéressant d'essayer.

Si vous souhaitez consigner le message envoyé à partir du code de votre application, le -forwardingTargetForSelector: tip fait partie de la solution.
Enveloppez votre objet:

@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

Maintenant, faites quelque chose comme ça:

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

et vous aurez un journal des envois de messages. Notez que les envois envoyés depuis l'objet intercepté ne seront pas interceptés, car ils seront envoyés en utilisant le pointeur 'self' de l'objet d'origine.

Si vous souhaitez uniquement consigner les messages appelés de l'extérieur (généralement appelés de délégués; pour voir quel type de message, quand, etc.), vous pouvez remplacer les réponses à réponse comme suit:

- (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;

}

Créez une sous-classe de NSProxy et implémentez -forwardInvocation: et -methodSignatureForSelector: (ou -forwardingTargetFor Sélecteur: , si vous le dirigez simplement vers un deuxième objet au lieu de manipuler vous-même la méthode).

NSProxy est une classe conçue pour implémenter -forwardInvocation: . Il existe quelques méthodes, mais vous ne voulez surtout pas qu'elles soient attrapées. Par exemple, la capture des méthodes de comptage des références empêcherait la désallocation du proxy, sauf sous garbage collection. Toutefois, s'il existe des méthodes spécifiques sur NSProxy que vous devez absolument transférer, vous pouvez les remplacer spécifiquement et appeler manuellement -forwardInvocation: . Notez que le fait qu’une méthode soit répertoriée dans la documentation NSProxy ne signifie pas que NSProxy l’implémente, mais qu’il est prévu que tous les objets mandatés l’auront.

Si cela ne vous convient pas, fournissez des détails supplémentaires sur votre situation.

Vous voulez peut-être utiliser la méthode de NSObject de -forwardInvocation . Cela vous permet de capturer un message, de le recibler puis de le renvoyer.

Vous pouvez utiliser l’appel de méthode avec l'un des vôtres, ce qui vous permet de faire tout ce que vous voulez sur "interception". et appelle à la mise en œuvre originale. Swizzling est fait avec class_replaceMethod ().

Un appel de méthode, no. Un message à envoyer, oui, mais vous allez devoir être beaucoup plus descriptif si vous voulez une bonne réponse quant à la procédure.

Pour faire quelque chose quand une méthode est appelée, vous pouvez essayer une approche basée sur les événements. Ainsi, lorsque la méthode est appelée, elle diffuse un événement qui est capté par tous les auditeurs. Je ne suis pas très doué avec l’objectif C, mais j’ai trouvé quelque chose de similaire avec NSNotificationCenter dans Cocoa.

Mais si par "intercepter" vous voulez dire "stop", alors vous avez peut-être besoin de plus de logique pour décider si la méthode doit être appelée.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top