无论如何都要监视特殊对象的方法调用并得到通知?我想在调用[object someMethod]时调用函数“Observer”。我知道我可以使用Objective c KVO监视对象的值变化,但它并没有告诉我调用堆栈(哪些函数)导致对象的值发生变化。有什么类似于KVO,但对于方法,我可以监视函数调用?如果现有框架无法实现这一功能,有关如何实现此功能的任何建议吗?谢谢!
答案 0 :(得分:11)
我假设您不控制要拦截其方法的类的实现,因此更改API为@Conrad Shultz建议不可行。我想到了其他一些方法。
首先是方法调配。这种方法的工作方式是使用类别向类中添加具有相同签名的方法,然后交换要拦截的方法的实现以及添加的方法。您添加的方法将在调用“自身”之前和/或之后调用您想要的任何挂钩。由于您交换了实现,因此调用“本身”实际上会调用原始方法。有很多很好的解释方法在那里徘徊。 Here's one.
另一种选择可能是将对象包装在NSProxy中并使用-forwardInvocation
传递调用。有很多资源在那里详细解释代理和调用转发,所以我不会在这里重复它。 Here's one.此方法的局限性在于您必须具有在代理中交换真实对象的必要访问权限。如果对象是在某个API中私有创建的,那么这种方法将没有用处。
另一种方法是做KVO所做的事情。键值观察的工作原理是在运行时创建类的子类,然后执行所谓的isa
- 调配以将现有实例的类更改为新的动态子类。没有理由你不能创建一个运行时生成的类,你想要拦截它的实例方法,然后让你的运行时生成的子类的实现调用超级类之前和/或之后的任何其他钩子。 (实际)方法的实现。这有可能让你更容易拦截一个以上的方法,但是不会有一种简单的方法来拦截任意签名的任意方法,因为你必须找到一种方法来调用你的钩子而不会破坏堆栈,然后矢量关闭到主要实现。以这种方式截取具有任意签名的任意方法可能涉及在程序集中编写trampolines然后尾调用实际实现,就像objc_msgSend那样。这是一个good article on runtime subclassing。
从历史的角度来看:还有一个名为poseAsClass的功能,它也允许这个,但它已被删除,我认为自SnowLeopard-ish时间框架以来,所以它可能是一个非首发。
这些选项中的任何一个都会产生性能影响并且都非常“聪明”。我不是说要背拍自己 - 我没有提出任何这些,只是反刍他们。但多年来我注意到,当一个解决方案感觉过于“聪明”时,这是一个明显的迹象,表明我正走向最终的灾难。换句话说,如果您正在使用的API没有为您提供拦截这些方法调用的方法,则可能表明您应该从不同的角度处理问题。但显然你的里程可能会有所不同。
答案 1 :(得分:1)
在related question中,比尔·布姆加纳(Bill Bumgarner)观察到(对于双关语者而言)这对于一般情况是不可能的。
我建议如果由于API设计需要知道调用者,那么更好的方法是修改你的方法以获取“sender”参数(就像许多框架方法一样,包括每个IBAction都这样做)。然后调用者有责任将self传递给方法。
这具有允许更复杂模式的额外优势。我可以想象,例如,一个合法地想要传递另一个对象而不是self的代理对象。 (显然,这需要谨慎和深谋远虑。)