这是我参与8月更文挑战的第28天,活动详情查看:8月更文挑战
关于OC的编译与运行
- 对于 C 语言,函数调用在编译的时候会决定调用哪个函数,编译完成之后直接顺序执行
- 对于 OC语言, 属于动态函数调用,在编译的时候并不能决定真正调用哪个函数 ,只有在真正运行的时候才会根据函数的名称找到对应的函数来调用
- 事实证明:在编译阶段,OC 可以调用任何函数,即使这个函数并未实现,只要声明过就不会报错。而 C 语言在编译阶段就会报错
Runtime简介
Runtime 简称运行时,Runtime 就是执行已编译好的代码,在其底层,OC 就是通过 Runtime 这个库把方法调用转化为消息发送
消息驱动机制
在 OC 中方法调用:
[object doSomething];
在编译时 RunTime 会将上述代码转化为:
objc_msgSend(id object,selector);
//object:方法的调用者
//selector:方法选择器
动态绑定
当我们在调用一个实例方法的时候,其过程:
-
Runtime 系统会把方法
[object doSomething];,调用转化为消息发送objc_msgSend(object, @selector (doSomething));,并且把方法的调用者和方法选择器,当做参数传递过去。objc_msgSend分为以下俩种:- 方法无参数:
objc_msgSend(id objec,selector); - 方法有参数:
objc_msgSend(id objec,selector, arg1.arg2...);
@selector (doSomething)返回一个SEL数据类型,即方法选择器。SEL主要作用是快速的通过方法名字(doSomething)查找到对应方法的函数指针,然后调用其函数。SEL其本身是一个int型的地址,地址中存放着方法的名字。在一个类中,每一个方法对应着一个SEL。iOS类中不能存在两个名称相同的方法,即使参数类型不同,因为SEL是根据方法名字生成的,相同的方法名称只能对应一个SEL。 - 方法无参数:
-
在
objc_msgSend函数中,首先通过obj的isa指针找到obj对应的class。在class中,有一块最近调用的方法的指针缓存,所以先去cache通过selector查找对应的method。 -
若
cache中未找到,再去method list中查找,若method list中未找到,则去superClass中查找。若能找到,则将method加入到cache中,以方便下次查找 -
通过
method中的函数指针跳转到对应的函数中去执行。
和RunTime交互的三种方式
-
通过Objective-C源代码
大部分情况下你就只管写你的Objc代码就行,Runtime 系统自动在幕后工作。
消息的执行会使用到一些编译器为实现动态语言特性而创建的数据结构和函数,Objc中的类、方法和协议等在 Runtime 中都由一些数据结构来定义。
-
通过Foundation框架中类NSObject的方法
Cocoa 中大多数类都继承于NSObject类,也就自然继承了它的方法。最特殊的例外是NSProxy,它是个抽象超类,它实现了一些消息转发有关的方法,可以通过继承它来实现一个其他类的替身类或是虚拟出一个不存在的类,说白了就是领导把自己展现给大家风光无限,但是把活儿都交给幕后小弟去干。
有的NSObject中的方法起到了抽象接口的作用,比如description方法需要你重载它并为你定义的类提供描述内容。NSObject还有些方法能在运行时获得类的信息,并检查一些特性,比如class返回对象的类;
isKindOfClass:和isMemberOfClass:则检查对象是否在指定的类继承体系中;respondsToSelector:检查对象能否响应指定的消息;conformsToProtocol:检查对象是否实现了指定协议类的方法;methodForSelector:则返回指定方法实现的地址。 -
通过直接调用运行时系统的函数
Runtime 系统是一个由一系列函数和数据结构组成,具有公共接口的动态共享库。头文件存放于/usr/include/objc目录下。许多函数允许你用纯C代码来重复实现 Objc 中同样的功能。虽然有一些方法构成了NSObject类的基础,但是你在写 Objc 代码时一般不会直接用到这些函数的,除非是写一些 Objc 与其他语言的桥接或是底层的debug工作。在中Objective-C Runtime Reference有对 Runtime 函数的详细文档。