19

我可以拦截 Objective-C 中的方法调用吗?如何?

编辑: Mark Powell的回答给了我一个部分解决方案,即-forwardInvocation方法。但是文档指出 -forwardInvocation 仅在向对象发送没有相应方法的消息时才调用。我希望在所有情况下都可以调用一个方法,即使接收者确实有那个选择器。

4

10 回答 10

24

你可以通过调配方法调用来做到这一点。假设你想获取 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@:");

您可以在 Objective C 运行时文档中获得更多详细信息。

于 2009-10-29T09:35:39.040 回答
15

在 Objective-C 中拦截方法调用(假设它是一个 Objective-C,而不是 C 调用)是通过一种称为方法调配的技术来完成的。

您可以在此处找到有关如何实现该功能的介绍。有关如何在实际项目中实现方法调配的示例,请查看OCMock(Objective-C 的隔离框架)。

于 2009-10-24T17:15:11.920 回答
11

在 Objective-C 中发送消息被转换为对函数objc_msgSend(receiver, selector, arguments)或其变体之一的调用objc_msgSendSuper, objc_msgSend_stret, objc_msgSendSuper_stret.

如果可以更改这些函数的实现,我们可以拦截任何消息。不幸的是,objc_msgSend它是 Objective-C 运行时的一部分,不能被覆盖。

通过谷歌搜索,我在 Google Books: A Reflective Architecture for Process Control Applications by Charlotte Pii Lunau上找到了一篇论文。该论文通过将对象的isa类指针重定向到自定义 MetaObject 类的实例来介绍一种 hack。因此,打算发送给修改对象的消息将发送到 MetaObject 实例。由于 MetaObject 类没有自己的方法,因此它可以通过将消息转发到修改后的对象来响应转发调用。

该论文不包括源代码中有趣的部分,我不知道这种方法是否会对 Cocoa 产生副作用。但尝试一下可能会很有趣。

于 2009-10-27T16:26:54.240 回答
5

如果要记录从应用程序代码发送的消息,-forwardingTargetForSelector: 提示是解决方案的一部分。
包装你的对象:

@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

现在做这样的事情:

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

您将有一个消息发送日志。请注意,从被拦截对象内部发送的发送不会被拦截,因为它们将使用原始对象“self”指针发送。

于 2011-08-20T06:53:02.480 回答
3

如果您只想记录从外部调用的消息(通常从委托中调用;查看哪种消息、何时等),您可以像这样覆盖 respondsToSelector:

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

}

于 2012-01-19T11:56:18.670 回答
2

创建一个子类NSProxy并实现-forwardInvocation:and -methodSignatureForSelector:(或者-forwardingTargetForSelector:,如果您只是将它指向第二个对象而不是自己摆弄该方法)。

NSProxy是一个为实现而设计的类-forwardInvocation:。它有一些方法,但大多数情况下你不希望它们被抓住。例如,捕获引用计数方法将阻止代理被释放,除非在垃圾回收下。但是,如果NSProxy您绝对需要转发某些特定方法,则可以专门覆盖该方法并-forwardInvocation:手动调用。请注意,仅仅因为NSProxy文档下列出了一个方法并不意味着NSProxy实现它,只是希望所有代理对象都有它。

如果这对您不起作用,请提供有关您的情况的更多详细信息。

于 2009-10-30T15:50:24.527 回答
1

也许你想要NSObject-forwardInvocation方法。这使您可以捕获消息,重新定位它,然后重新发送它。

于 2009-10-24T16:54:45.833 回答
1

您可以使用您自己的方法调用来调整方法调用,它可以在“拦截”上执行您想要执行的任何操作并调用原始实现。Swizzling 是通过 class_replaceMethod() 完成的。

于 2009-10-24T17:06:22.627 回答
0

方法调用,不。发送一条消息,是的,但如果你想要一个关于如何发送的好答案,你将不得不更具描述性。

于 2009-10-24T16:57:27.473 回答
-1

要在调用方法时执行某些操作,您可以尝试基于事件的方法。因此,当调用该方法时,它会广播一个事件,该事件被任何侦听器拾取。我对目标 C 不是很好,但我只是在 Cocoa 中使用NSNotificationCenter找到了类似的东西。

但是,如果“拦截”的意思是“停止”,那么也许您需要更多的逻辑来决定是否应该调用该方法。

于 2009-10-24T16:43:04.977 回答