Objective-C中的拦截方法调用

时间:2009-10-24 16:38:26

标签: objective-c methods intercept

我可以拦截Objective-C中的方法调用吗?怎么样?

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

10 个答案:

答案 0 :(得分:23)

你可以通过调用方法调用来实现。假设您想要获取所有版本到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运行时documentation中获取更多详细信息。

答案 1 :(得分:15)

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

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

答案 2 :(得分:11)

在Objective-C中发送消息被转换为函数objc_msgSend(receiver, selector, arguments)或其变体objc_msgSendSuperobjc_msgSend_stretobjc_msgSendSuper_stret的调用。

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

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

本文不包括源代码的有趣内容,我不知道这种方法是否会在Cocoa中产生副作用。但尝试可能会很有趣。

答案 3 :(得分:5)

如果要记录应用程序代码中的消息发送,-forwardingTargetForSelector:tip是解决方案的一部分。
包裹你的物体:

@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”指针发送。

答案 4 :(得分: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;

}

答案 5 :(得分:2)

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

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

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

答案 6 :(得分:1)

也许你想要NSObject的{​​{1}}方法。这允许您捕获消息,重新定位它然后重新发送它。

答案 7 :(得分:1)

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

答案 8 :(得分:0)

方法调用,没有。发送一条消息,是的,但是如果你想要一个很好的答案,那么你将需要更具描述性。

答案 9 :(得分:-1)

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

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