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 ? '+' : '-',
objectsClass,
implementingClass,
sel_getName(selector));
objcMsgLogLock.lock();
write (objcMsgLogFD, buf, strlen(buf));
objcMsgLogLock.unlock();
// Tell caller to not cache the method
return false;
}
当objcMsgLogEnabled
为true
,可以调用logMessageSend
函数,打印整个方法调用流程。添加代码
extern void instrumentObjcMessageSends(BOOL flag);
instrumentObjcMessageSends(YES);
....
instrumentObjcMessageSends(NO);
进入tmp
文件夹查看打印结果,
可以看到,除了动态方法决议的两个函数resolveClassMethod
和resolveInstanceMethod
外,程序还执行了forwardingTargetForSelector
、methodSignatureForSelector
和doesNotRecognizeSelector
函数。
forwardingTargetForSelector
在苹果文档里面查看forwardingTargetForSelector
函数说明
Returns the object to which unrecognized messages should first be directed.
这个函数可以为方法指定另一个接收者,来接收该方法。
测试,新建类LKPerson
和LKStudent
,在LKPerson
中添加方法sayHello
,在LKStudent
中实现方法sayHello
。
@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;
}
打印结果-[LKStudent sayHello]
程序并没有崩溃,并且打印方法[LKStudent sayHello]
。
methodSignatureForSelector
和forwardInvocation
在苹果文档里面查看methodSignatureForSelector
函数说明,
Returns an NSMethodSignature object that contains a description of the method identified by a given selector.
这个方法必须和forwardInvocation
函数同时调用。
methodSignatureForSelector
函数给方法返回一个签名,forwardInvocation
函数来重新解读这个方法。
测试
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
if (aSelector == @selector(sayHello)) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}
-(void)forwardInvocation:(NSInvocation *)anInvocation{
LKStudent *p = [LKStudent alloc];
anInvocation.target = p;
anInvocation.selector = @selector(studentSayHello);
[anInvocation invoke];
}
打印结果-[LKStudent studentSayHello]
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
给方法重新签名anInvocation.target = p;
重新指定方法接收者anInvocation.selector = @selector(studentSayHello);
重新指定方法SEL
doesNotRecognizeSelector
+ (void)doesNotRecognizeSelector:(SEL)sel {
_objc_fatal("+[%s %s]: unrecognized selector sent to instance %p",
class_getName(self), sel_getName(sel), self);
}
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.
当经过慢速查找,快速查找,动态方法决议以及消息转发流程之后,仍然找到方法,会抛出异常,调用doesNotRecognizeSelector
,打印错误日志。