解决方法
首先是方法调配.这种方法的工作方式是使用类别向类添加具有相同签名的方法,然后交换要拦截的方法的实现以及添加的方法.您添加的方法将在调用“自身”之前和/或之后调用您想要的任何挂钩.由于您交换了实现,因此调用“本身”实际上会调用原始方法.有很多很好的解释方法在那里徘徊. Here’s one.
另一种选择可能是将对象包装在NSProxy中并使用-forwardInvocation传递调用.有很多资源在那里详细解释代理和调用转发,所以我不会在这里重复它. Here’s one.此方法的局限性在于您必须具有对代理中交换真实对象的必要访问权限.如果对象是在某个API中私有创建的,那么这种方法将没有用处.
另一种方法是做KVO做的事情.键值观察的工作原理是在运行时创建类的子类,然后执行所谓的isa-swizzling,将现有实例的类更改为新的动态子类.没有理由你不能创建一个运行时生成的类,你想要拦截它的实例方法,然后让你的运行时生成的子类的实现调用超级类之前和/或之后的任何其他钩子. (实际)方法的实现.这有可能让你更容易拦截一个以上的方法,但是不会有一种简单的方法来拦截任意签名的任意方法,因为你必须找到一种方法来调用你的钩子而不会破坏堆栈,然后矢量关闭到主要实现.以这种方式截取具有任意签名的任意方法可能涉及在程序集中编写trampolines然后尾调用实际实现,就像objc_msgSend那样.这是good article on runtime subclassing.
从历史的角度来看:还有一个名为poseAsClass的功能,它也允许这个,但它已被删除,我认为自SnowLeopard-ish时间帧以来,所以它可能是一个非首发.
任何这些选项都会产生性能影响并且都非常“聪明”.我不是说要背拍自己 – 我没有提出任何这些,只是反刍他们.但多年来我注意到,当一个解决方案感觉过于“聪明”时,这是一个迹象表明我将走向最终的灾难.换句话说,如果您正在使用的API没有为您提供拦截这些方法调用的方法,则可能表明您应该从不同的角度处理问题.但显然你的里程可能会有所不同.