Runtime触探学习笔记
- runtime是一套API,由C和C++汇编一起写成的API,给我们的OC提供运行时.
运行时
- 运行时,代码在起来后装载内存,运行时功能就依赖于runtime
编译时
- 正在编译的时间,编译过程的主要工作就是将源代码翻译成可以识别的语言,我们使用的比如OC,Swift,java等属于高级语言,需要翻译成机器语言(最底层的二进制)
1.首先我们要从OC对象的本质开始
- OC的面向对象都是基于C/C++的结构体来实现的,OC对象、类的实质就是一个结构体
typedef struct objc_object SYPerson;
typedef struct {} _objc_exc_SYPerson;
struct SYPerson_IMPL {
struct NSObject_IMPL NSObject_IVARS;
};
- 方法的本质就是发送消息,消息:消息接受者 消息编号...参数(消息体)
objc_msgSend(s,sel_registerName("run"));
对于C函数而言就是静态性,如果我编译一个不存在的run函数就会直接报错,但是OC在编译阶段并不会报错,只会在运行时报错。比如在一个person对象中的.m文件中并没有实现run方法,但是在编译阶段不会错误,一旦运行后就会崩溃
clang使用方式
clang -rewrite-objc main.m -o main.cpp
clang运行项目main会生成一个cpp文件,cpp文件中有下面这段关键代码
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
SYPerson *person = ((SYPerson *(*)(id, SEL))(void *)objc_msgSend)((id)((SYPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("SYPerson"), sel_registerName("alloc")), sel_registerName("init"));
((void (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("run"));
run();
}
return 0;
}
-
(void *)objc_msgSend)((id)person
消息接收者 -
sel_registerName("run"))
方法编号 -
imp
函数实现的指针 ---sel -> imp 如何进行下层通讯
方法下层消息如何走的?对象方法和类方法分别是如何发起的?父类发送消息?
- runtime的三种调用方式
1.api
2.NSObject (isKindOf)
3.OC上层(@selector)
对象发送消息
SYPerson *me = [SYPerson new];
[me run];
((void (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("run"));
类方法在元类中以实例方法的姿态存在,类在元类中也是以实例存在
id cls = [SYDog class];
void *pointA = &cls;
[(__bridge id)pointA walk];
((void (*)(id, SEL))(void *)objc_msgSend)(objc_getClass("SYDog"), sel_registerName("walk"));
objc_msgSend 发送消息的两种方式
- 快速:直接通过汇编中的缓存cache_t(imp哈希表)中去找,每一个类中都会缓存
- 慢速:直接通过C/C++ 汇编一起完成的,找到后又存在缓存中
- 为什么objc_msgSend要用汇编写是因为C语言写一个函数无法通过保留未知的参数跳转到任意的一个指针。第二个原因就是更快。
网友评论