1. 在 objc/runtime.h 中,Class(类)被定义为指向objc_class 结构体的指针,objc_class 结构体的数据结构如下:
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;
struct objc_class {
Class _Nonnull isa; // objc_class 结构体的实例指针
#if !__OBJC2__
Class _Nullable super_class; // 指向父类的指针
const char * _Nonnull name; // 类的名字
long version; // 类的版本信息,默认为 0
long info; // 类的信息,供运行期使用的一些位标识
long instance_size; // 该类的实例变量大小;
struct objc_ivar_list * _Nullable ivars; // 该类的实例变量列表
struct objc_method_list * _Nullable * _Nullable methodLists; // 方法定义的列表
struct objc_cache * _Nonnull cache; // 方法缓存
struct objc_protocol_list * _Nullable protocols; // 遵守的协议列表
#endif
};
- objc_class 结构体 存放的数据称为 元数据(metadata)。
- objc_class 结构体 的第一个成员变量是 isa 指针,isa 指针保存的是所属类的结构体的实例的指针,这里保存的就是 objc_class 结构体的实例指针,而实例换个名字就是 对象。换句话说,Class(类) 的本质其实就是一个对象,我们称之为 类对象。
2.objc/objc.h 中关于 Object(对象) 的定义
Object(对象)被定义为objc_object 结构体,其数据结构如下:
/// Represents an instance of a class.
struct objc_object {
Class _Nonnull isa; // objc_object 结构体的实例指针
};
/// A pointer to an instance of a class.
typedef struct objc_object *id;
- id 被定义为一个指向 objc_object 结构体 的指针。从中可以看出 objc_object 结构体 只包含一个 Class 类型的 isa 指针。
- 换句话说,一个 Object(对象)唯一保存的就是它所属Class(类) 的地址。当我们对一个对象,进行方法调用时,比如 [receiver selector];,它会通过objc_object 结构体的isa 指针 去找对应的 object_class 结构体,然后在 object_class 结构体 的 methodLists(方法列表) 中找到我们调用的方法,然后执行。
3. Meta Class(元类)
从上边我们看出,对象(objc_object 结构体) 的isa 指针指向的是对应的类对象(object_class 结构体)。那么类对象(object_class 结构体)的 isa 指针又指向什么呢?
- object_class 结构体 的 isa 指针 实际上指向的的是 类对象 自身的Meta Class(元类)。
- Meta Class(元类) 就是一个类对象所属的 类。一个对象所属的类叫做 类对象,而一个类对象所属的类就叫做 元类。
- Runtime 中把类对象所属类型就叫做Meta Class(元类),用于描述类对象本身所具有的特征,而在元类的 methodLists 中保存了类的方法链表,即「类方法」。并且类对象中的 isa 指针 指向的就是元类。
- 每个类对象有且仅有一个与之相关的元类。
4.实例对象(object)、class(类)、Meta Class(元类)之间的关系
- 水平方向上,每一级中的 实例对象 的 isa 指针 指向了对应的类对象,而类对象的isa 指针指向了对应的元类。而所有元类的 isa 指针最终指向了 NSObject 元类,因此 NSObject 元类 也被称为根源类。
- 垂直方向上, 元类的 isa 指针 和 父类元类 的 isa 指针 都指向了根元类。而根源类的 isa 指针 又指向了自己。
- 类对象的父类指针指向了 父类的类对象,父类的类对象又指向了根类的类对象,根类的类对象最终指向了nil。
- 元类 的 父类指针 指向了 父类对象的元类。父类对象的元类 的 父类指针指向了 根类对象的元类,也就是根元类。而根元类的父类指针指向了根类对象,最终也指向了nil。
5.最后说下Method(方法)
object_class 结构体的methodLists(方法列表)中存放的元素就是Method(方法)。
在objc/runtime.h 中,表示Method(方法) 的 objc_method 结构体 的数据结构:
/// An opaque type that represents a method in a class definition.
/// 代表类定义中一个方法的不透明类型
typedef struct objc_method *Method;
struct objc_method {
SEL _Nonnull method_name; // 方法名
char * _Nullable method_types; // 方法类型
IMP _Nonnull method_imp; // 方法实现
};
- objc_method 结构体 中包含了方法名(method_name),方法类型(method_types) 和方法实现(method_imp)。
- SEL method_name; // 方法名
/// An opaque type that represents a method selector.
typedef struct objc_selector *SEL;
SEL 是一个指向 objc_selector 结构体的指针,只是一个保存方法名的字符串。
SEL sel = @selector(viewDidLoad);
NSLog(@"%s", sel); // 输出:viewDidLoad
SEL sel1 = @selector(testSEL);
NSLog(@"%s", sel1); // 输出:testSEL
- IMP method_imp; // 方法实现
/// A pointer to the function of a method implementation.
#if !OBJC_OLD_DISPATCH_PROTOTYPES
typedef void (*IMP)(void /* id, SEL, ... */ );
#else
typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...);
#endif
IMP 的实质是一个函数指针,所指向的就是方法的实现。IMP用来找到函数地址,然后执行函数。
char * method_types; // 方法类型
方法类型 method_types是个字符串,用来存储方法的参数类型和返回值类型。Method 就是将 SEL(方法名) 和 IMP(函数指针) 关联起来,当对一个对象发送消息时,会通过给出的 SEL(方法名) 去找到 IMP(函数指针) ,然后执行。