instrumentObjcMessageSends
void instrumentObjcMessageSends(BOOL flag)
{
bool enable = flag;
// Shortcut NOP
if (objcMsgLogEnabled == enable)
return;
// If enabling, flush all method caches so we get some traces
if (enable)
_objc_flush_caches(Nil);
// Sync our log file
if(objcMsgLogFD ! = -1)
fsync (objcMsgLogFD);
objcMsgLogEnabled = enable;
}
static void log_and_fill_cache(Class cls, IMP imp, SEL sel, id receiver, Class implementer){#if SUPPORT_MESSAGE_LOGGING
if (slowpath(objcMsgLogEnabled && implementer)) {
bool cacheIt = logMessageSend(implementer->isMetaClass(),
cls->nameForLogging(),
implementer->nameForLogging(),
sel);
if(! cacheIt)return;
}
#endif
cls->cache.insert(sel, imp, receiver);
}
bool logMessageSend(bool isClassMethod,
const char *objectsClass,
const char *implementingClass,
SEL selector)
{
char buf[ 1024 ];
// Create/open the log file
if (objcMsgLogFD == (-1))
{
snprintf (buf, sizeof(buf), "/tmp/msgSends-%d", (int) getpid ());
objcMsgLogFD = secure_open (buf, O_WRONLY | O_CREAT, geteuid());
if (objcMsgLogFD < 0) {
// no log file - disable logging
objcMsgLogEnabled = false;
objcMsgLogFD = -1;
return true; }}// Make the log entry
snprintf(buf, sizeof(buf), "%c %s %s %s\n",
isClassMethod ? '+' : The '-',
objectsClass,
implementingClass,
sel_getName(selector));
objcMsgLogLock.lock();
write (objcMsgLogFD, buf, strlen(buf));
objcMsgLogLock.unlock();
// Tell caller to not cache the method
return false;
}
Copy the code
When objcMsgLogEnabled is true, the logMessageSend function can be called to print out the entire method call flow. Add code
extern voidinstrumentObjcMessageSends(BOOL flag); instrumentObjcMessageSends(YES); . instrumentObjcMessageSends(NO);Copy the code
Go to the TMP folder to view the printed result.
As you can see, there are two functions except dynamic method resolutionresolveClassMethod
andresolveInstanceMethod
The program is also executedforwardingTargetForSelector
,methodSignatureForSelector
anddoesNotRecognizeSelector
Function.
forwardingTargetForSelector
In apple document view forwardingTargetForSelector function description
Returns the object to which unrecognized messages should first be directed.
This function can specify another receiver for the method to receive the method. Create classes LKPerson and LKStudent, add method sayHello to LKPerson, and implement method sayHello to LKStudent.
@interface LKStudent : NSObject
@end
@implementation LKStudent
- (void)sayHello{
NSLog(@"%s",__func__);
}
@end
@interface LKPerson : NSObject
- (void)sayHello;
@end
@implementation LKPerson
- (id)forwardingTargetForSelector:(SEL)aSelector{
return [LKStudent alloc];
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
LKPerson *person = [LKPerson alloc];
[person sayHello];
}
return 0; } print result -[LKStudent sayHello]Copy the code
The program does not crash and prints the method [LKStudent sayHello].
methodSignatureForSelector
andforwardInvocation
In apple document view methodSignatureForSelector function specification,
Returns an NSMethodSignature object that contains a description of the method identified by a given selector.
This method must be called simultaneously with the forwardInvocation function. MethodSignatureForSelector function method returns to a signature, forwardInvocation function to reinterpret this method. test
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
if (aSelector == @selector(sayHello)) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [supermethodSignatureForSelector:aSelector]; } - (void)forwardInvocation:(NSInvocation *)anInvocation{ LKStudent *p = [LKStudent alloc]; anInvocation.target = p; anInvocation.selector = @selector(studentSayHello); [anInvocation invoke]; } Print result -[LKStudent studentSayHello]Copy the code
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
Re-sign the methodanInvocation.target = p;
Respecify the method receiveranInvocation.selector = @selector(studentSayHello);
Respecify methodSEL
doesNotRecognizeSelector
+ (void)doesNotRecognizeSelector:(SEL)sel {
_objc_fatal("+[%s %s]: unrecognized selector sent to instance %p",
class_getName(self), sel_getName(sel), self);
}
Copy the code
NSObject’s implementation of forwardInvocation: simply invokes the doesNotRecognizeSelector: method; it doesn’t forward any messages. Thus, if you choose not to implement forwardInvocation:, sending unrecognized messages to objects will raise exceptions.
When after slow search, fast search, dynamic method resolution and message forwarding process, still find the method, will throw an exception, call doesNotRecognizeSelector, print error log.