Objective-C的面向对象语法源于Smalltalk消息传递风格。所有其他非面向对象的语法,包括变量类型,预处理器(preprocessing),流程控制,函数声明与调用皆与C语言完全一致。
Objective-C 语言最大的特点,相对于C而言继承了c许多语法特性,也增加来了不同的东西,如类定义和方法的定义。
官方文档对于Objective-C 的介绍中还提及到了一个动态类型检查和动态绑定(dynamic typing and binding)
Objective-C runtime 说的就是这个东西,即是程序在运行时可以动态进行类型检查 和动态绑定。
必要的一些概念,可以先跳过,遇到再回来查询
SEL 方法选择器。使用一个名字指定了对象可使用的方法,如果浅显理解,可以看成函数指针,但并非函数指针。因为函数指针必须指定有函数名称,参数个数与具体类型,函数返回值,三要素缺一不可,而SEL,仅仅指定了函数名字和参数的多少,可以理解成记录了一个函数名字和参数多少的标识符。 objc里面初始化一个sel时使用的语法,@selector(method:)就可以看到,这里指定了一个函数名叫method,带有一个参数的方法选择器。细心的可以看到,既然方法选择器只纪录了函数名和参数个数,那如何跳转到函数的实现中,执行函数?
IMP 函数实现(method implementation) ,其实就是函数指针
啥是动态类型检查和动态绑定?
动态类型检查(dynamic typing )指的是对象的类型的检测这个步骤,只有在运行时才会进行
动态绑定(dynamicbinding/late binding )指的是函数的调用由程序在运行时动态指定。
一个对象调用函数到具体过程:
因为objc从c继承而来,条条框框其实没有逃离c的具体实现,objc的函数实现,归根到底其实还是c的函数实现,只是在c的基础上加多了一套动态绑定,动态类型检查的机制。
1. 调用函数
对象receiver调用doMethod函数
[receiver doMethod] |
程序运行时(runtime)将此过程转换成调用objc_msgSend函数 ,即向实例对象发送一个消息(messaging) ,消息的内容,就是一个方法选择器
objc_msgSend(receiver, selector) |
函数有多个参数时,是这样子的
objc_msgSend(receiver, selector, arg1, arg2, ...) |
3. 根据方法选择器(SEL)查找实现地址
实例对象接受到消息后,根据方法选择器,查询到具体到实现地址,把第二步中的参数传入函数的具体实现中,执行具体实现,最终完成执行,返回数据。
在方法选择器的概念说明中提到,方法选择器只指定了方法名和参数个数,并没有指定具体的实现(method implementation)。
那runtime是如何实现从方法选择器到具体实现的映射?
对于每一个类的结构,都包含了两个必不可少多元素 。
指向父类的指针(A pointer to the superclass)
类分发表(A class dispatch table姑且叫函数分发表把)
类分发表(A class dispatch table姑且叫函数分发表把)
当实例对象收到消息后,指向自身到isa 指针会在类结构中的类分发表中查询对应的方法选择器对应实现地址,如果当前对象的类结构中没有找到对应的方法选择器,指针则指向父类指针,再次寻找对应的方法选择器。这也就是在实际的应用中,子类重载父类方法,子类调用函数会优先调用的原因。
objc_msgSend找到方法实现地址后,除了调用具体的实现,将参数传递过去之外,还传递了两个参数
接受对象(函数调用者) 和 函数的selector
在函数体中,我们可以使用self,_cmd直接调用这个函数
上文说的方法的具体实现,其实说的就是c里面的函数指针
ObjC 里面的 IMP 和 函数指针 之间便可以互相转换
在objc/objc.h 里面有 IMP的定义