we sometimes want to observe a invocation of selector so that we could perform pre-process or post-process to the call automatically.
this is not difficult if the target is a custom class in the observation. There are serval approaches to achieve this goal. However, it is difficult to do it for ANY class. for example, observe [NSViewController -initWithNibName:bundle:], [NSObject -init]
What I tried to do is to have something like:
void observe(Class clazz, SEL op, isClassMethod, CALLBACK pre, CALLBACK post);
in order to do this, I need to define:
id replacedMethod(id target, SEL op, ...);
void replacedMethod2(id target, SEL op, ...);
...
and then inside of the observe function above, I get the original implementation of the class and put it into a map.
WFFuncWrap *wrap;
NSString *key;
...
wrap = [[WFFuncWrap alloc] init];
wrap->_isClassMethod = isClassMethod;
if (isClassMethod) {
wrap->_method = class_getClassMethod(clazz, op);
}else{
wrap->_method = class_getInstanceMethod(clazz, op);
}
wrap->_func = method_getImplementation(wrap->_method);
[_dict setObject:wrap forKey:key];
...
and after that, I use the 'class_replaceMethod' function to replace the original implementation of the class with one of the above 'replacedMethod' functions.
inside of these 'replacedMethod' functions, i invoke the callbacks at the begin and the end. in theory, i only need to look up the original implementation and put it in between the 'pre' and the 'post' in order to achieve the goal. However, that is the difficult part I cannot get it solve. it is difficult because of the signature the original implementation is not fixed.
I did not found a way to call func(id, SEL, ...) in side of another gunc(id, SEL, ...). Actually, I belief assemble code will help to solve the problem, but that is sth that I am not familiar with. I tried, but it is too complex.
id replacedMethod(id obj, SEL op, ...){
CALLBACK pre, post;
IMP originalImp;
id retObj;
...
pre(obj, op);
//call original implementation, HOW ?
post(obj, op);
return retObj;
}
It there any idea to solve this problem? Thanks a lot!!
A completely different way to do what you are trying to do, so I don't know if this will help, is to create a proxy class that can forward all methods invoked onto it to a target class (look at NSProxy), you can then use -[NSObject forwardInvocation:] which gives you all of the arguments packaged in a NSInvocation object. At runtime your proxy object can pass itself of as pretty much any other class, their may be some edge cases, because your proxy class can override methods like -[NSObject isKindOfClass:], -[NSObject respondsToSelector:] etc.
You can then do anything you want before and after the method invocation.