Runtime简介Objective-C runtime 运行 native code 。Java VM 运行 byte code。 原生码(native code)又称为机器码(machine code),是电脑的CPU可直接解读的数据,是计算机可以直接执行,执行速度最快的代码。 字节码(byte code)码是一种中间状态(中间码)的二进制代码(文件)。需要转译后才能成为机器码。 比如java:字节码在运行时通过JVM(JAVA虚拟机)做一次转换生成机器指令,因此能够更好的跨平台运行。 Objc Runtime其实是一个Runtime库,它基本上是用C和汇编写的,这个库使得C语言有了面向对象的能力。 在Runtime中,对象可以用C语言中的结构体表示,而方法可以用C函数来实现,另外再加上了一些额外的特性。这些结构体和函数被runtime函数封装后,让OC的面向对象编程变为可能。 当程序执行[object doSomething]时,会向消息接收者(object)发送一条消息(doSomething),runtime会根据消息接收者是否能响应该消息而做出不同的反应。 动态语言静态类型语言:指在编译期间就去做数据类型检查,多数静态类型语言要求在使用变量之前必须声明数据类型。比如C#,Java。 动态类型语言:指在运行期间才去做数据类型检查,也就是说,用动态语言编程时,永远不用去给任何变量去指定数据类型。该语言会在你第一次给该变量赋值的时候,在内部把数据类型记录下来。JavaScript、ruby或者Python是典型的动态类型的语言。 OC语言,编译阶段并不能决定真正调用哪个函数,只要函数声明过即使没有实现也不会报错。 我们常说OC是一门动态语言,就是因为它总是把一些决定性的工作从编译阶段推迟到运行时阶段。OC代码的运行不仅需要编译器,还需要运行时系统(Runtime)来执行编译后的代码。 Runtime是一套底层纯C语言API,OC代码最终都会被编译器转化为运行时代码,通过消息机制决定函数调用方式,这也是OC作为动态语言使用的基础。 isa在Objective-C中,任何类的定义都是对象。类和类的实例(对象)没有任何本质上的区别。任何对象都有isa指针。 isa:是类指针,之所以说isa是指针是因为Class其实是一个指向objc_class结构体的指针,而isa 是它唯一的私有成员变量,即所有对象都有isa指针(isa位置在成员变量第一个位置) objc_class是个结构体,而且第一个成员变量也是isa指针,这个指针指向的Class不是指向自己而是metaclass(元类) metaclass每个实例对象也就是struct objc_object结构体它的isa的指针指向类对象Class,而Class里也有个isa的指针, 指向metaClass(元类)。 元类是类对象的类,类对象是元类的实例。 这时候相对于元类,objc_class就是元类的实例对象。 基于这种设计模式,不难发现:
看图说话: 1. objc_object中的isa指的是对象的类。 2. objc_class中的isa指的是类的元类。 3. superClass是一层层集成的,到最后NSObject的superClass是nil。而NSObject的isa指向根元类,这个根元类的isa指向它自己,而它的superClass是NSObject,也就是最后形成一个环。 4. metaClass也是相互继承的。 5. 从objc_class结构体可以看出来,里面有个isa属性,还有个super_class属性,它俩都是指针,其实在objc_class的定义中也能看出来,每一个objc_class都有isa,但是不一定会有super_class。 重点总结: meta-class也是一个类,也可以向它发送一个消息,那么它的isa又是指向什么呢?为了不让这种结构无限延伸下去,Objective-C的设计者让所有的meta-class的isa指向基类的meta-class,以此作为它们的所属类。即,任何NSObject继承体系下的meta-class都使用NSObject的meta-class作为自己的所属类,而基类的meta-class的isa指针是指向它自己。这样就形成了一个完美的闭环。 方法在 objc-runtime-new.h中定义: struct method_t { SEL name; const char *types; IMP imp; struct SortBySELAddress : public std::binary_function 我们说下上面代码中的SEL和IMP。 SEL:表示该方法的名称, IMP: 指向该方法的具体实现的函数指针。 SEL 的含义: typedef struct objc_selector *SEL; 它是一个指向 objc_selector 指针,表示方法的名字/签名。如下所示,打印出 selector,会打印出来方法的名称。 不同类的实例对象performSelector相同的 selector 时,会在各自的消息选标(selector)/实现地址(address) 方法链表中根据 selector 去查找具体的方法实现IMP, 然后用这个方法实现去执行具体的实现代码。这是一个动态绑定的过程,在编译的时候,我们不知道最终会执行哪一些代码,只有在执行的时候,通过selector去查询,我们才能确定具体的执行代码。 IMP 的含义: 在前面我们也看到 IMP 的定义为: typedef id (*IMP)(id, SEL, ...); id是一个指向 objc_object 结构体的指针,任何继承自 NSObject 的类对象都可以用id 来指代,因为 NSObject 的第一个成员实例就是isa。 IMP 是一个函数指针,这个被指向的函数包含一个接收消息的对象id(self 指针), 调用方法的选标 SEL (方法名),以及不定个数的方法参数,并返回一个id。也就是说 IMP 是消息最终调用的执行代码,是方法真正的实现代码 。我们可以像在C语言里面一样使用这个函数指针。 NSObject 类中的methodForSelector:方法就是这样一个获取指向方法实现IMP 的指针,methodForSelector:返回的指针和赋值的变量类型必须完全一致,包括方法的参数类型和返回值类型。 IMP实际上是一个函数指针,指向方法实现的地址。 Method用于表示类定义中的方法,则定义如下: typedef struct objc_method *Methodstruct objc_method{ SEL method_name OBJC2_UNAVAILABLE; // 方法名 char *method_types OBJC2_UNAVAILABLE; IMP method_imp OBJC2_UNAVAILABLE; // 方法实现} 我们可以看到该结构体中包含一个SEL和IMP,实际上相当于在SEL和IMP之间作了一个映射。有了SEL,我们便可以找到对应的IMP,从而调用方法的实现代码。 |
|