runtime初探
对象与方法的本质
runtime-消息发送
runtime-动态方法解析
runtime-消息转发
runtime
runtime是由C,C++,汇编一起写成的一套api
而OC提供了运行时
运行时和编译时
编译时:为你正在编译的时间,把源代码翻译成可以识别的语言,OC,Swift,JAVA都是高级语言,不能被机器识别,所以需要编译成相应的机器语言二进制,
因为二进制比较难写代码,所以才延伸出高级语言让我们编译
运行时:当代码跑起来后,会被装载到内存,当编译一个项目后,product里面会有一个app,打开包内容里面会看到被编译到沙盒里的可执行文件
当代码装载到内存后,就活了
下面看运行时功能
C和C++汇编提供给运行时带来的OC功能
OC提供运行时,当一个方法没有实现时,在编写代码时,C语言就会报错,但是OC不会,只会在运行时报错
OC对象的本质是一个结构体
方法的本质是发送消息objc_msgSend(参数1为消息接收者,参数2为消息编号),通过SEL去找到IMP
消息的组成:
(id)p消息接收者
sel_registerName("run")方法编号
任何一个方法调用,在底层都是一个类似于如此的
SEL _cmd为方法编号
IMP为函数实现的指针
void runIMP(id self,SEL _cmd){
}
当调用某个方法时,其实底层是
LGPerson *p = [[LGPerson alloc] init];
[p run];
//原理
LGPerson *p = ((LGPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("LGPerson"), sel_registerName("new"));
((void (*)(id, SEL))(void *)objc_msgSend)((id)p, sel_registerName("run"));
#解说
任何方法的调用都会被编译成objc_msgSend方法
第一句代码是将objc_msgSend方法强转成((LGPerson *(*)(id, SEL))类型,参数一id为对象(id)objc_getClass("LGPerson"),参数二SEL为消息是sel_registerName("new")
runtime的三种调用方式
1.runtime的api
2.NSObject会提供的api
3.编译器提供的OC上层方法,类似于@selector
sel_registerName("run")等于@selector(run)
对象方法编译底层
LGStudent *s = [LGStudent new];
[s run];
objc_msgSend(s, sel_registerName("run"));
类方法编译底层
id cls = [LGStudent class];
void *pointA = &cls;
[(__bridge id)pointA walk];
objc_msgSend(objc_getClass("LGStudent"), sel_registerName("walk"));
向父类发消息(对象方法)
struct objc_super mySuper;
mySuper.receiver = s;
mySuper.super_class = class_getSuperclass([s class]);
objc_msgSendSuper(&mySuper, @selector(run));
向父类发消息(类方法)
struct objc_super myClassSuper;
myClassSuper.receiver = [s class];
myClassSuper.super_class = class_getSuperclass(object_getClass([s class]));// 元类
objc_msgSendSuper(&myClassSuper, sel_registerName("walk"));
对象方法存在类中,是一个实例方法
类方法存在元类中,也是一个实例方法
objc_msgSend 是汇编写成的
有两种方式
1.快速:在缓存中直接通过汇编
2.慢速:C配合C++以及汇编一起完成的
缓存来源于类
看下类结构,bits是所有的数据,每个类里面都有cache,用来存储方法和IMP,IMP和@selector会组成一张哈希表

当我们调用一个方法时
1.先找cache
2.通过SEL去哈希表中找IMP
3.若是没找到,就会用慢速(C配合C++以及汇编一起完成)去找,找到后会去缓存中存一份,方便下次查找
为什么是汇编写的
- C语言不可能通过写一个函数来直接保留未知的参数,然后跳转到任意的指针,而objc_msgSend是运行时,通过传进来的对象和@selector去查找的,C无法实现
2.汇编很快,汇编有寄存器,在底层可以直接编译
消息发送objc_msgSend源码分析流程
![]()
动态方法解析_源码分析流程
当消息调用时,如果

消息转发 作用
可以在forwardingTargetForSelector中进行
1.自定义处理
2.收集crash
3.防止崩溃
可以在forwardInvocation中
1.系统调用堆栈,存到沙盒里发给服务器,类似友盟收集崩溃
2.切面编程,埋点统计
runtime应用
self不一定是当前类,谁调用是谁
网友评论