赞
踩
在之前的几篇博客里面,已经对OC类的底层结构进行了分析,并对内部主要的成员变量(isa/bits)做了详细的分析。在上两个博客
iOS底层探索之类的结构—cache分析(上)
iOS底层探索之类的结构—cache分析(下)
对类中的cache
做了比较详细的分析。后面通过断点
查看汇编
可以发现在insert
方法调用流程之前,还有一个cache
读取流程,objc_msgSend
和 cache_getImp
。这就涉及到Runtime
的知识点了,之前的内容都是承上启下的,是互相关联的。
runtime
翻译过来称为运行时,与之对应的是编译时
。大部分的iOS开发人员,都听过runtime
这个词,也知道运行时。但只是停留在表面,只是知道而已,并没有去深入的去探索和分析过。
OC
语言是一门动态语言
,拥有动态语言的三大特性:动态类型
、动态绑定
、动态加载
。而底层实现就是熟悉又陌生的Runtime
。
运行时
是一种面向对象的编程语言(面向对象编程)的运行环境。运行时表明了在某个时间段内,哪个程序正在运行。运行时是计算机程序运行生命周期内的一个阶段,其它阶段还包括:编译时、链接时和加载时。简单理解就是, 代码跑起来,被装载到内存中的过程。(你的代码保存在磁盘上没装入内存之前是个死家伙,只有跑到内存中才变成活的)。编译时
顾名思义就是正在编译的时候。那什么叫编译
呢?就是编译器帮你把源代码翻译成机器能识别的二进制代码 。Java
只有JVM
识别的字节码,C#
中只有CLR
能识别的MSIL
。另外还有链接器、汇编器、为了了便于理解我们可以统称为编译器)关键字
了。词法分析
,语法分析之类的过程。就像个老师检查学生的作文中有没有错别字和病句一样 。Xcode
时,点下build
那就开始编译。errors
或者warning
信息,那都是编译器检查出来的。这时的错误就叫编译时
错误,这个过程中做的类型检查也就叫编译时类型检查
,或静态类型检查
(所谓静态嘛就是没把真把代码放内存中运行起来,而只是把代码当作文本来扫描下)。runtime
的使用的三种方式,其三种实现方法与编译层和底层的关系如图所示
通过OC
上层的代码实现,例如 [JPerson hello]
通过NSObject
方法实现,例如isKindOfClass
通过Runtime API
底层方法实现,例如class_getInstanceSize
图中的
compiler
就是编译器,就是我们熟悉的LLVM
在之前的一篇博客iOS开发之结构体底层探索我们知道平时写的OC
代码,底层实现其实都是C/C++
的代码实现的,再经过编译器LLVM
编译,最终转化为机器语言。
通过clang
编译的源码,理解了OC
对象的本质(结构体
),同样的,我们也可以使用clang
命令编译成main.cpp
文件,看看方法的本质
是什么?
@interface JPPerson : NSObject @property (nonatomic, readwrite , copy) NSString *personName; - (void)superTest; @end @implementation JPPerson - (void)superTest { NSLog(@"这是父类"); } @end @interface JPStudent : JPPerson @property (nonatomic, readwrite , copy) NSString *studentName; - (void)test; @end @implementation JPStudent - (void)test { NSLog(@"%s",__func__); } @end int main(int argc, const char * argv[]) { @autoreleasepool { JPStudent *stu = [[JPStudent alloc]init]; [stu test]; [stu superTest]; } return 0; }
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
JPStudent *stu = ((JPStudent *(*)(id, SEL))(void *)objc_msgSend)((id)((JPStudent *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("JPStudent"), sel_registerName("alloc")), sel_registerName("init"));
((void (*)(id, SEL))(void *)objc_msgSend)((id)stu, sel_registerName("test"));
((void (*)(id, SEL))(void *)objc_msgSend)((id)stu, sel_registerName("superTest"));
}
return 0;
}
通过上述代码可以看出,OC
的方法调用,底层变成了objc_msgSend
,也就是我们熟悉的消息发送
我们可以通过模仿objc_msgSend
方法来实现,[stu test]
的调用
从控制台的输出可以看到,是一模摸一样样
。
由此可以断定 [stu test]
等价于objc_msgSend(stu,sel_registerName("test"))
注意
:不能直接调用
objc_msgSend
,需要导入头文件#import <objc/message.h>
需要将
target --> Build Setting -->
搜索msg
– 将enable strict checking of obc_msgSend calls
由YES
改为NO
,将严厉的检查机制关掉,否则objc_msgSend
的参数会报错。
#import <objc/message.h>
NO
objc_msgSend
(消息的接受者,消息的主体(sel + 参数))
在上面
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。