Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Objective C - get notified when a method is called

is there anyway to monitor a special object's method call and get notified? I would like to have a function "Observer" gets called when [object someMethod] is invoked. I know I can monitor an object's value change using objective c KVO, but it doesn't tell me the call stack (which functions) caused the object's value to change. Is there something similar to KVO, but for methods, where I can monitor function calls? Any suggestions on how to implement such feature if this is not possible with the existing framework? Thanks!

like image 681
clx Avatar asked Feb 12 '12 09:02

clx


2 Answers

I'm assuming you don't control the implementation of the class whose method you want to intercept, and so changing the API as @Conrad Shultz suggests isn't feasible. There are several other approaches that come to mind.

The first is method swizzling. The way this works is you add a method with the same signature to the class using a category, then you swap the implementations of the method you want to intercept and the method you added. The method you add will call whatever hooks you want before and/or after calling through to "itself." Since you swap the implementations, calling "itself" actually calls the original method. There are plenty of good explanations of method swizzling out there. Here's one.

Another option might be to wrap the object in an NSProxy and pass calls through using -forwardInvocation. Again there are many resources out there explaining proxies and invocation forwarding in detail, so I won't rehash it here. Here's one. This approach is limited in that you have to have the necessary access to swap in your proxy for the real object. If the object is created privately inside some API, then this approach won't be useful.

Another approach is to do what KVO does. Key-Value Observing works by creating a subclass of an class at runtime and then doing what's called isa-swizzling to change the class of the existing instance to the new dynamic subclass. There's no reason you couldn't create a runtime-generated subclass of the class whose instance method you want to intercept, and then have your runtime-generated subclass's implementation of that method call out to whatever other hooks before and/or after calling the superclass's (real) implementation of the method. This has the potential to let you intercept more than just one method more easily, but there's not going to be a straightforward way to intercept arbitrary methods with arbitrary signatures, because you've got to figure out a way to call your hooks without mangling the stack, and then vector off into the primary implementation. Intercepting arbitrary methods with arbitrary signatures this way would likely involve writing the trampolines in assembly and then tail calling into the real implementation, like objc_msgSend does. Here's a good article on runtime subclassing.

For historical perspective: there also used to be a feature called poseAsClass that would also allow this but it has been removed, I think since SnowLeopard-ish timeframe, so it's probably a non-starter.

Any of these options are going to have performance implications and are all exceedingly "clever". I don't say that to pat myself on the back -- I didn't come up with any of these, only regurgitated them. But I've noticed over the years that when a solution feels overly "clever", it's a telltale sign that I'm headed for eventual disaster. Put differently, if the API you're working with doesn't give you a means to intercept these method calls, it's probably a sign that you should approach the problem from a different angle. But your mileage may vary, obviously.

like image 144
ipmcc Avatar answered Nov 13 '22 06:11

ipmcc


In a related question, Bill Bumgarner observes (no pun intended) that this is not possible for the general case.

I would suggest that if you need to know the caller due to your API design, the better approach is to modify your method to take a "sender" parameter (as many of the framework methods, including every IBAction, do). It is then the caller's responsibility to pass self to the method.

This has the additional advantage of allowing more complex patterns. I could imagine, for example, a proxy object legitimately wanting to pass another object in instead of self. (Obviously this would need to be done with care and forethought, though.)

like image 35
Conrad Shultz Avatar answered Nov 13 '22 05:11

Conrad Shultz