runtime基本知识
每次都有人问,什么是id
,class
,object
,ivar
…这里做一个总结啦..
class
1 | /// An opaque type that represents an Objective-C class. |
class
是objc_class
这个结构体的指针
id
1 | typedef struct objc_object *id; |
id
是objc_object
这个结构体的指针
objc_object
1 | /// Represents an instance of a class. |
objc_class
1 | struct objc_class : objc_object { |
ivar
1 | /// An opaque type that represents an instance variable. |
ivar 是指向objc_ivar结构体的指针.
objc_ivar
1 | struct objc_ivar { |
objc_ivar
这个是记录属性的名字,类型,地址偏移量,大小
综合以上可以得出:
总结
通过上面这几个定义,可以理解成:
class
只是一个指向objc_class
的指针objc_class
是继承至objc_object
的结构体,除了继承了父类的// Class ISA;
成员变量,还增加了其他的成员变量objc_object
一个只有Class ISA;
成员变量的结构体这个是一个基类
例如当声明一个Person
实例对象时,实际上是创建了一个指向objc_class
结构体的指针.
更新
objc_object
被typedef成为id类型.objc_class
继承自objc_object,这里包含了isa_t类型的结构体isa,还有父类指针(superclass),方法缓冲(cache),实例方法链表(bits).object
类和NSObject类都包含一个objc_class
的isa.
类,对象方法调用
- 当一个对象的实例方法被调用的时候,会通过isa找到对应的类,然后在该类的class_data_bits_t(即bits)中去查找方法.class_data_bits_t(即bits)是指向了类对象的数据区域,在这里查找相应方法的对应实现.
- 当调用的是类方法时,引入了元类的概念(meta-class).
- 对象的实例方法调用时,通过对象的isa在类获取方法实现,类对象的类方法调用时,通过类的isa获取元类中的方法实现.
- 在
meta-class
中,存储了一个类中所有的类方法.每个类都有一个单独的meta-class
.
借用霜神的描述
实线是superclass的指针,虚线是isa指针
- root class 实际就是NSObject,NSObject没有超类,所以他的superclass为nil
- 每一个Class都有一个唯一指向的Metaclass.
- rootclass(meta)的superclass指向rootclass(class),即NSObject,形成回路.
- 每一个metaclass的isa都指向rootClass(meta).
- 类对象和元类对象都是唯一的,对象可以在运行时创建无数个.在main函数之前,类对象和元类对象已经被创建出来
访问一个类的ivar
首先在回到objc_class
来,因为这objc_class
中才能够拿到ivar.objc_class
中有一个class_rw_t *data() {
的成员变量,这里保存的是// 主要的运行数据,他是class_rw_t
这个类型的:
1 | struct class_rw_t { |
在class_rw_t
这个结构体中,有一个class_ro_t
这里放的是运行数据的副本.
1 | struct class_ro_t { |
这里有一个ivar_list_t
,这里存放的就是当前类中所有成员变量的ivar.
1 | struct ivar_list_t { |
这里的ivar_t
就是每个ivar
1 | struct ivar_t { |
通过以上的结论,想要访问ivar
,需要多次的指针转义:
class -> class.(class_rw_t *data()) -> class.(class_rw_t *data()).(class_ro_t *ro) -> class.(class_rw_t *data()).(class_ro_t *ro).(ivar_list_t * ivars) -> class.(class_rw_t *data()).(class_ro_t *ro).(ivar_list_t * ivars).first[n]
使用简单案例试验
1 | #import <Foundation/Foundation.h> |
将代码转成cpp,整理后如下:
1 | typedef void(*MyBlock)(void); |
- 从上面的代码可以看到,对于每一个成员变量,都会有一个对应的全局变量:
1 | extern "C" unsigned long OBJC_IVAR_$_MyObject$_haha; |
- blcok_invoke对应的实现通过对象本事作为基地址+全局变量偏移对变量的ivar进行赋值的
1 | static void __MyObject__inits_block_func_0(struct __MyObject__inits_block_impl_0 *__cself) { |
- block的构造函数确实捕捉了
self
1 | struct __MyObject__inits_block_impl_0 { |
objc_msgSend
- (id)perform:(SEL)aSelector with:anObject
源码如下:
1 | - (id)perform:(SEL)aSelector with:anObject |
在调用objc_msgSend
时候,需要提前指定好返回值,调用者,方法名,传入参数:
((id()(id, SEL, id)) //(id()返回值;(id, SEL, id)调用者,方法名,传入参数类型
objc_msgSend) // 调用的方法为objc_msgSend
(self, aSelector, anObject) // 调用者,方法名,传入参数