Objective C - 在调用方法时得到通知

clx*_*clx 8 runtime objective-c

反正有没有监视特殊对象的方法调用并得到通知?我想在调用[object someMethod]时调用函数"Observer".我知道我可以使用Objective c KVO监视对象的值变化,但它并没有告诉我调用堆栈(哪些函数)导致对象的值发生变化.有什么类似于KVO,但对于方法,我可以监视函数调用?如果现有框架无法实现这一功能,有关如何实现此功能的任何建议吗?谢谢!

ipm*_*mcc 11

我假设您不控制要拦​​截其方法的类的实现,因此更改API为@Conrad Shultz建议不可行.还有其他一些方法可以想到.

首先是方法调配.这种方法的工作方式是使用类别向类中添加具有相同签名的方法,然后交换要拦截的方法的实现以及添加的方法.您添加的方法将在调用"自身"之前和/或之后调用您想要的任何挂钩.由于您交换了实现,因此调用"本身"实际上会调用原始方法.有很多很好的解释方法在那里徘徊.这是一个.

另一种选择可能是将对象包装在NSProxy中并通过using传递调用-forwardInvocation.有很多资源在那里详细解释代理和调用转发,所以我不会在这里重复它.这是一个.这种方法的局限性在于您必须具有在代理中交换真实对象的必要访问权限.如果对象是在某个API中私有创建的,那么这种方法将没有用处.

另一种方法是做KVO所做的事情.键值观察的工作原理是在运行时创建类的子类,然后执行所谓的isa-swizzling,将现有实例的类更改为新的动态子类.没有理由你不能创建一个运行时生成的类,你想要拦截它的实例方法,然后让你的运行时生成的子类的实现调用超级类之前和/或之后的任何其他钩子. (实际)方法的实现.这有可能让你更容易拦截一个以上的方法,但是不会有一种简单的方法来拦截任意签名的任意方法,因为你必须找到一种方法来调用你的钩子而不会破坏堆栈,然后矢量关闭到主要实现.以这种方式截取具有任意签名的任意方法可能涉及在程序集中编写trampolines然后尾调用实际实现,就像objc_msgSend那样.这是关于运行时子类化好文章.

对于历史角度:还有一个名为poseAsClass的功能,它也允许这个但它已被删除,我认为自SnowLeopard-ish时间帧以来,所以它可能是一个非首发.

任何这些选项都会产生性能影响并且都非常"聪明".我不是说要背拍自己 - 我没有提出任何这些,只是反刍他们.但多年来我注意到,当一个解决方案感觉过于"聪明"时,这是一个明显的迹象,表明我正走向最终的灾难.换句话说,如果您正在使用的API没有为您提供拦截这些方法调用的方法,则可能表明您应该从不同的角度处理问题.但显然你的里程可能会有所不同.


Con*_*ltz 1

在一个相关问题中,Bill Bumgarner 观察到(没有双关语)这对于一般情况是不可能的。

我建议,如果您由于 API 设计而需要了解调用者,更好的方法是修改您的方法以采用“sender”参数(正如许多框架方法,包括每个 IBAction 所做的那样)。然后调用者有责任将 self 传递给该方法。

这具有允许更复杂的模式的额外优点。例如,我可以想象,一个代理对象合法地想要传递另一个对象而不是 self。(显然,这需要谨慎和深思熟虑地完成。)