我可以拦截Objective-C中的方法调用吗?怎么样?
修改 Mark Powell 的回答给了我一个部分解决方案, -forwardInvocation 方法。 但是文档指出-forwardInvocation仅在向对象发送消息时才调用,该消息没有相应的方法。我想在所有情况下调用一个方法,即使接收器确实有那个选择器。
答案 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_msgSendSuper
,objc_msgSend_stret
,objc_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找出类似的东西。
但是如果通过“拦截”你的意思是“停止”,那么也许你需要更多的逻辑来决定应该调用该方法。