上篇文章分析了慢速查找流程,若是递归完父类任然没有找到imp
,就将imp = forward_imp
,由于c++
const IMP forward_imp = (IMP)_objc_msgForward_impcache;
复制代码
objc
源码全局搜索_objc_msgForward_impcache
程序员
汇编中重点看一下
__objc_forward_handler
的实现。编程
全局搜索_objc_forward_handler
(注意去掉一个下划线),来到c++
代码:markdown
最终(消息转发流程)找不到方法实现,报错并打印报错信息。 找不到方法最后报错流程:
_objc_msgForward_impcache
->_objc_forward_handler
->_objc_fatal
函数
在慢速查找过程当中,若是没有查找到imp
,而且没有执行过动态方法决议就执行一次动态方法决议源码分析
在慢速查找过程lookUpImpOrForward
中,执行了一次动态方法决议:post
//第一次:0011 & 0010 = 0010 (知足条件,只有一次机会)
//第二次:0000 & 0010 = 0000 (不知足条件)
if (slowpath(behavior & LOOKUP_RESOLVER)) {
//0010 ^ 0010 = 0000 = behavior
behavior ^= LOOKUP_RESOLVER;
return resolveMethod_locked(inst, sel, cls, behavior);
}
复制代码
resolveMethod_locked
定义:性能
static NEVER_INLINE IMP resolveMethod_locked(id inst, SEL sel, Class cls, int behavior) {
runtimeLock.assertLocked();
ASSERT(cls->isRealized());
runtimeLock.unlock();
// 程序员 你是否是傻 没有这个方法 - imp-nil
// 奔溃 - 友善
// 给你一次机会 拯救地球 -> imp
if (! cls->isMetaClass()) {
// try [cls resolveInstanceMethod:sel]
//对象方法
resolveInstanceMethod(inst, sel, cls);
}
else {
// try [nonMetaClass resolveClassMethod:sel]
// and [cls resolveInstanceMethod:sel]
//类方法
resolveClassMethod(inst, sel, cls);
//元类中的方法是以对象方法的形式存在的
if (!lookUpImpOrNilTryCache(inst, sel, cls)) {
resolveInstanceMethod(inst, sel, cls);
}
}
// chances are that calling the resolver have populated the cache
// so attempt using it
// lookUpImpOrForwardTryCache->_lookUpImpTryCache->_lookUpImpTryCache->lookUpImpOrForward
//再次慢速查找一次
return lookUpImpOrForwardTryCache(inst, sel, cls, behavior);
}
复制代码
对象方法决议resolveInstanceMethod
实现ui
static void resolveInstanceMethod(id inst, SEL sel, Class cls) {
runtimeLock.assertUnlocked();
ASSERT(cls->isRealized());
//若是cls中实现了resolveInstanceMethod
SEL resolve_sel = @selector(resolveInstanceMethod:);
///若是cls中没有实现resolveInstanceMethod,系统默认也会有一个实现,因此下面不会return
if (!lookUpImpOrNilTryCache(cls, resolve_sel, cls->ISA(/*authenticated*/true))) {
// Resolver not implemented.
return;
}
//系统会给cls中resolveInstanceMethod发送消息
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
bool resolved = msg(cls, resolve_sel, sel);
// Cache the result (good or bad) so the resolver doesn't fire next time.
// +resolveInstanceMethod adds to self a.k.a. cls
IMP imp = lookUpImpOrNilTryCache(inst, sel, cls);
省略部分代码
}
复制代码
对象方法决议:若是cls
中实现了resolveInstanceMethod
,系统会给resolveInstanceMethod
发送消息,若是没有实现,系统也会有个resolveInstanceMethod
默认实现。 spa
1.准备调试代码:
#import <Foundation/Foundation.h>
@interface ABPerson : NSObject
-(void)doSomething;
@end
@implementation ABPerson
@end int main(int argc, const char * argv[]) {
@autoreleasepool {
ABPerson *p = [ABPerson new];
[p doSomething];
NSLog(@"Hello, World!");
}
return 0;
}
复制代码
2.执行结果必然报错: 3.按照对象方法决议源码分析,修改
ABPerson
实现代码
#import <objc/message.h>
@interface ABPerson : NSObject
-(void)doSomething;
@end
@implementation ABPerson
-(void)saySomething
{
NSLog(@"%s",__func__);
}
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
NSLog(@"resolveInstanceMethod--------- :%@-%@",self,NSStringFromSelector(sel));
if (sel == @selector(doSomething)) {
IMP sayImp = class_getMethodImplementation(self, @selector(saySomething));
Method method = class_getInstanceMethod(self, @selector(saySomething));
const char *type = method_getTypeEncoding(method);
return class_addMethod(self, sel, sayImp, type);
}
return [super resolveInstanceMethod:sel];
}
复制代码
4.执行结果再也不报错,而且成功将调用doSomething
转为调用saySomething
。
static void resolveClassMethod(id inst, SEL sel, Class cls) {
runtimeLock.assertUnlocked();
ASSERT(cls->isRealized());
ASSERT(cls->isMetaClass());
//若是没有实现resolveClassMethod,return,不过系统默认实现了一个,这里不会return
if (!lookUpImpOrNilTryCache(inst, @selector(resolveClassMethod:), cls)) {
// Resolver not implemented.
return;
}
Class nonmeta;
{
mutex_locker_t lock(runtimeLock);
nonmeta = getMaybeUnrealizedNonMetaClass(cls, inst);
// +initialize path should have realized nonmeta already
//没有实现元类报错
if (!nonmeta->isRealized()) {
_objc_fatal("nonmeta class %s (%p) unexpectedly not realized",
nonmeta->nameForLogging(), nonmeta);
}
}
//系统会给nonmeta(元类)中resolveClassMethod发送消息
//nonmeta(元类)中的方法是以对象方法的形式存在的
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
bool resolved = msg(nonmeta, @selector(resolveClassMethod:), sel);
// Cache the result (good or bad) so the resolver doesn't fire next time.
// +resolveClassMethod adds to self->ISA() a.k.a. cls
IMP imp = lookUpImpOrNilTryCache(inst, sel, cls);
省略部分代码
}
复制代码
类方法决议:系统会给nonmeta(元类)
中resolveClassMethod
发送消息,若是没有实现,会给元类的父类(元类的父类的父类直到NSObject
为止) 发消息。系统也会有个resolveClassMethod
默认实现。
1.准备调试代码:
#import <Foundation/Foundation.h>
#import <objc/message.h>
@interface ABPeople : NSObject
+ (void)doSomething;
@end
@implementation ABPeople
@end int main(int argc, const char * argv[]) {
@autoreleasepool {
[ABPeople doSomething];
NSLog(@"Hello, World!");
}
return 0;
}
复制代码
2.执行结果必然报错:
3.按照对象方法决议源码分析,修改ABPeople
实现代码
#import <objc/message.h>
@interface ABPeople : NSObject
+ (void)doSomething;
@end
@implementation ABPeople
+ (void)saySomething
{
NSLog(@"%s",__func__);
}
+ (BOOL)resolveClassMethod:(SEL)sel
{
NSLog(@"resolveClassMethod--------- :%@-%@",self,NSStringFromSelector(sel));
if (sel == @selector(doSomething)) {
IMP sayImp = class_getMethodImplementation(objc_getMetaClass("ABPeople"), @selector(saySomething));
Method method = class_getInstanceMethod(objc_getMetaClass("ABPeople"), @selector(saySomething));
const char *type = method_getTypeEncoding(method);
return class_addMethod(objc_getMetaClass("ABPeople"), sel, sayImp, type);
}
return [super resolveClassMethod:sel];
}
@end
复制代码
4.执行结果再也不报错,而且成功将调用doSomething
转为调用saySomething
。
咱们知道了类方法是在元类中是以实力方法的方式存在的,元类的父类一直往上找是会找到NSObject
的,那么NSObject
分类也是可以实现类方法决议的,类方法决议代码修改:
#import <Foundation/Foundation.h>
#import <objc/message.h>
@interface ABPeople : NSObject
//实力方法
- (void)doSomething;
//类方法
+ (void)doSomething;
@end
@implementation ABPeople
@end
@interface NSObject (Cate) @end @implementation NSObject (Cate) -(void)saySomething {
NSLog(@"%s",__func__);
}
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
NSLog(@"resolveInstanceMethod--------- :%@-%@",self,NSStringFromSelector(sel));
if (sel == @selector(doSomething)) {
IMP sayImp = class_getMethodImplementation(self, @selector(saySomething));
Method method = class_getInstanceMethod(self, @selector(saySomething));
const char *type = method_getTypeEncoding(method);
return class_addMethod(self, sel, sayImp, type);
}
return NO;
}
@end int main(int argc, const char * argv[]) {
@autoreleasepool {
[ABPeople doSomething];
NSLog(@"Hello, World!");
}
return 0;
}
复制代码
仍然能够成功运行。类别方式的决议一样适用于实例方法
经过NSObject
类别的方式可以全局监听方法找不到,经过 if (sel == @selector(doSomething))
,能够将特定方法,如doSomething
进行后续的处理,如上传日志等。 这样作就可以对原来的代码无侵入,动态的注入代码。可是这样作也会带来相应的性能消耗,如过多的if (sel == @selector(doSomething))
判断,同时截断了后面的消息转发流程。 因此不建议在动态方法决议阶段作AOP
。
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);
//flag赋值给objcMsgLogEnabled
objcMsgLogEnabled = enable;
}
复制代码
objcMsgLogEnabled
在logMessageSend
函数中的使用:
bool objcMsgLogEnabled = false;
static int objcMsgLogFD = -1;
bool logMessageSend(bool isClassMethod, const char *objectsClass, const char *implementingClass, SEL selector) {
char buf[ 1024 ];
// Create/open the log file
if (objcMsgLogFD == (-1))
{
//写下日志,路径为:"/tmp/msgSends-%d", (int) getpid ()
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;
}
复制代码
instrumentObjcMessageSends
使用很简单,只须要三步:
1.导出函数供外部使用:
extern void instrumentObjcMessageSends(BOOL flag);
复制代码
2.在须要跟踪的函数上下加上instrumentObjcMessageSends
,如:
ABPeople *p = [ABPeople new];
instrumentObjcMessageSends(YES);
[p doSomething];
instrumentObjcMessageSends(NO);
复制代码
3.文件夹前往路径:/tmp/msgSends
resolveInstanceMethod
(对象方法实现),resolveClassMethod
(类方法实现),经过此方法,能够进行后续的处理,若是没有实现将进入消息转发流程。isa
继承链,方法找不到,会一直找到NSObjct
,经过NSObjct
类别实现对象动态方法决议,能够进行全局监听,进行AOP
,但不建议在这个阶段作AOP
。