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!!
Thanks for Nathan Day, who gave me the idea of using NSProxy. It is a clean solution to my problem if impact to performance is no need to consider seriously. I did not take NSProxy as my solution, because performance is considered seriously in my project.
forwarding a call to another function without knowing its signature is difficult in c language. Actually, there is no way to call a function real don't know its the signature, because in that case, we will not be able to know where to read and how to pass the arguments.
I found that, different cpu architectures and compilers have different calling conventions. gcc compiler on 32 bit cpu is easier to be handled since the arguments are stored one by one in order. You could read them and pass them once you know their types, order and basic address.
However, gcc for 64bit cpu stores the arguments in a complex way. integers, flow points and overflowed arguments are stored in 3 different segments in the memory. structs arguments could stored in all three segments in different situations. Only knowing the types, order and basic address will not be enough to find out the address of the arguments, but also need the exact rule of the convention. I found the rules of convention. However, there is no document to prove that what I found is actually what the compiler does. Moreover, that requires process the runtime method description, something like: '@32@0:8@16{?=id}24', which is not easy to be parse.
If there is an approach could tell the exact offsets of the arguments, then my problem could be solved. There is a function 'method_getArgumentInfo', looks like what I was looking for. However, it is not available in OBJC2
Fortunately, I finally found that the debug description of the NSMethodSignature class could help me to find out where are the argument located by providing a string description that marks down the offsets of the arguments:
And by the assist of NSInvocation and class_replaceMethod I can finally find the solution. The weakness is that there is no guarantee that the description of NSMethodSignature will always describe the accurate informations.
the following code segment is the detail implementation of the solution. NOTE 1. this is not a complete implementation, but just show the idea of doing this. 2. this code only tested with x64 cpu 3. in order to handle any selector, it need some more agent functions to handle different return types