Runtime底层原理分析
Runtime是一套API,有两个版本的:
一个是legacy Version版本。早期版本Objective-C 1.0 32位的Mac OSX的平台
一个是Modern Version版本。现行版本iPhone程序和Mac OS X V10.5以后的64系统
Objective-C程序有三种途径和运行时系统交互
- 通过 Objective-C源代码
- 通过Foundation框架中的 NSObject的方法
- 通过调用运行时系统提供给我们的API接口
对象方法和类方法
Person *p = [[Person alloc] init];
[p run];
clang 分析
终端命令 clang -rewrite-objc main.m -o main.cpp
typedef struct objc_object Person;
typedef struct {} _objc_exc_Person;
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
Person *p = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc")), sel_registerName("init"));
((void (*)(id, SEL))(void *)objc_msgSend)((id)p, sel_registerName("run"));
// (void (*)(id, SEL))(void *)objc_msgSend)((id)p。 消息的接受者
// sel_registerName("run") 方法编号
}
return 0;
}
Person编译成一个objc_object的结构体,OC对象的本质就是一个结构体
任何的方法调用的本质都是: 都会编译成objc_msgSend方法
• 对象方法发送消息
objc_msgSend(p,sel_registerName("run"));
NSLog(@"%p---%p",sel_registerName("run"),@selector(run));
// 0x7fff356b2cc2---0x7fff356b2cc2
• 类方法发送消息
objc_msgSend(objc_getClass("LGStudent"),sel_registerName("walk"));
• 向父类发送消息(对象方法)
struct objc_super cus;
cus.receiver = p;
cus.super_class = class_getSuperclass([p class]);
objc_msgSendSuper(&cus, @selector(run));
• 向父类发送消息(类方法)
struct objc_super cusSuper;
cusSuper.receiver = [p class ];
cusSuper.super_class = class_getSuperclass(objc_getClass([p class]));
objc_msgSendSuper(&cusSuper, @selector(run));
objc_getClass([p class]):代表元类
- 对象方法是存在类上的
- 类方法是存在元类上的
- 对象和类分在在类和元类上都是以实例存在的,所以对象当发和实例方法都是以实例方法的形式存在的
消息查找&转发
汇编部分
• objc_msgSend
• LNilOrTagged tagged pointer 存储一些简单数据:NSNumber NSDadte 如果指针小于等于 LNilOrTagged 直接return返回
• LGetLsaDone isa 处理完毕
• CacheLookup 是个宏定义 NORMAL 缓存查找imp
• call imp
• objc_msgSend_uncached
• CacheHit -> CALL
• CheckMiss ->__objc_msgSend_uncached
• 1 GETIMP-> LGetImpMiss
• 2 NORMAL->__objc_msgSend_uncached->_class_lookupMethodAndLoadCache3 C函数
• 3 LOOKUP->__objc_msgLookup_uncached
• add
• MethodTableLookup
• bl __class_lookupMethodAndLoadCache3
runtime-汇编.png
动态方法解析
No implementation found. Try method resolver once.只会执行一次
• 对象方法 _class_resolveInstanceMethod
• 类方法 _class_resolveClassMethod
void _class_resolveMethod(Class cls, SEL sel, id inst)
{
if (! cls->isMetaClass()) {//不是元类
// try [cls resolveInstanceMethod:sel]
_class_resolveInstanceMethod(cls, sel, inst);
}
else {//是元类
// try [nonMetaClass resolveClassMethod:sel]
// and [cls resolveInstanceMethod:sel]
_class_resolveClassMethod(cls, sel, inst);
if (!lookUpImpOrNil(cls, sel, inst,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/))
{
_class_resolveInstanceMethod(cls, sel, inst);
}
}
}
当我们实现这个方法的时候
+ (BOOL)resolveInstanceMethod:(SEL)sel{
NSLog(@"hellow");
return [super resolveInstanceMethod:sel];
}
动态方法解析.png
其实执行了两次,为什么呢
动态方法解析调用.png
static void _class_resolveInstanceMethod(Class cls, SEL sel, id inst){
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
bool resolved = msg(cls, SEL_resolveInstanceMethod, sel);
}
因为系统默认发送了一次消息:给当前类发送一个SEL_resolveInstanceMethod的消息.
另外说明一点:
Method hellowordM1= class_getClassMethod(self, hellowordSEL);
Method hellowordM= class_getInstanceMethod(object_getClass(self), hellowordSEL);
• 类方法是存在元类上的
• 对象方法是存在类上面的
• 都是以实力对象的方式存在的
• 类的类方法和元类的对象方法是一样的
标准消息转发流程
_objc_msgForward_impcache
通过源码可以发现只有汇编的调用没有实现,但是我们可以通过instrumentObjcMessageSends 输出系统函数的调用
extern void instrumentObjcMessageSends(BOOL);
int main(int argc, const char * argv[]) {
@autoreleasepool {
instrumentObjcMessageSends(YES);
[LGPerson walk];
instrumentObjcMessageSends(NO);
}
return 0;
}
运行之后可以发现在Macintosh HD ▸ private ▸ tmp路径下面会发现一个msgSends-54285的文件
消息转发的流程查看.png
从这里可以发现消息转发的流程:
• resolveInstanceMethod
• forwardingTargetForSelector
• methodSignatureForSelector
• forwardInvocation
• doesNotRecognizeSelector
实际的应用场景:
• 自定义事件处理。crash的手机 调用的堆栈信息,保存沙盒,上传服务器
• aop切面变成。aspect
• 数组越界的处理
• 消息转发..
• 运行时动态创建一些控制器等等,需要隐藏的页面