Object-C Runtime:对象、类、元类以及方法

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(函数指针) ,然后执行。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容