参考:
NSObject
协议中定义的多种方法,是 OC 代码与 Runtime 发生交互的重要位置。
NSObject 基类
这个类与 NSObject
协议定义在了一个文件 Public Headers/NSObjects.h
中:
|
|
其中:
OBJC_ROOT_CLASS
是一个宏:1 2 3 4 5 6 7
#if !defined(OBJC_ROOT_CLASS) # if __has_attribute(objc_root_class) # define OBJC_ROOT_CLASS __attribute__((objc_root_class)) # else # define OBJC_ROOT_CLASS # endif #endif
它为
NSObject
这个类附加了objc_root_class
这个属性,这是 GNU C 的特性,见:AttributeOBJC_EXPORT
也是一个宏,它标记了这个类可以被任意外部位置访问到;isa
是一个变量名,Class
是一个变量类型,OBJC_ISA_AVAILABILITY
也是一个宏,在 ObjC2 版本以后,isa
这个变量被标记为过期的属性,具体在objc_object
中实现(下面详细解释)。
Class
与 id
的定义在 Public Headers/objc.h
这个文件中:
|
|
objc_class
或 Class
这个类是所有Objective-C 类的夫类。
在 Objc2 之前,该类的定义在 Public Headers/runtime.h
中,参考意义不大就不列举了,就是一个简单的字段组合。在 2006 年苹果公司发布 Objcive-C 2.0 之后,该类的定义在 Public Headers/objc-runtime-new.h
这个文件中:
|
|
superclass
是用于指向夫类的指针(NSObject
中也有一个同名的类方法);cache
是方法缓存;class_data_bits_t
是一个封装了比特位的类,bits
是这个类对应实例的方法链表。
这个类主要封装了与面向对象相关的方法,比如继承、哈希化等。
objc_object
或 id
这个类是包括 object_class
在内的所有实例对象的夫类。
它定义在了文件 Project Headers/objc-private.h
,是一个不对外暴露的私有类(对外暴露为 id
):
|
|
isa
这个变量是这个类最核心的功能,它用于指定实例实现的类。观察函数也会发现,它主要实现 dealloc
/ retain
/ release
等析构、初始化、内存管理相关的工作。
小结:MetaClass
由上面的分析我们得出一个重要的结论:Objective-C 中类也是一个对象,这个对象是单例模式。实际上这些单例模式的对象是在 main 函数执行之前,从 dyld
到 runtime
这期间创建的。
既然 OC 类派生自 objc_object
,它就一定有 isa
这个成员,那么 OC 类的 isa
指针应该指向哪里呢?
这就牵涉到 isa
最重要的作用:消息传递。这里简单介绍一下消息传递的流程:
- 当源代码向一个对象发送消息时,会首先查找这个这个对象的
isa
参数; isa
参数指向一个类,Runtime 查找这个类的方法链表中查找并执行这个方法;
因此为了使 Objective-C
中的类也可以同样接受消息,Objective-C
将类方法分离了出去,称作元类(即 Meta Class)。Instance、Class、Meta-Class 三者的关系大致如下图:
其中的 superclass 为 nil
的 Root class 即为 NSObject
。NSObject
本质是 objc_class
这个类的单例。
isa_t
之前也提到了,在 Objective-C 中比较核心的 isa
,它的类型为 isa_t
。它的实现如下:
|
|
class_data_bits_t
objc_class
的一个成员的类型为 class_data_bits_t
,它的声明如下:
|
|
这个结构体只有一个 64 位的成员变量 bits
。而它最重要的两个类方法也列举了出来:data
、setData
。
bits
的内存布局可以用下面的示意图表示:
is_swift
:指示持有它的objc_class
是不是一个 swift 语言的类;has_default_rr
:指示持有它的objc_class
有没有默认实现retain/release/autorelease
等方法;
class_rw_t
与 class_ro_t
一个 objc_class
类中的属性、方法还有遵循的协议信息都保存在了 class_rw_t
中:
|
|
其中还有一个类型为 class_ro_t
指向常量的指针 ro
,其中存储了当前类在编译期就已经确定的属性、方法以及遵循的协议:
|
|
在编译之后,
class_data_bits_t.data
直接指向class_ro_t
,也就是说
class_ro_t
是编译时产生的;在 Runtime 加载了对应的
objc_class
之后,class_data_bits_t.data
与class_ro_t
的指向关系中插入了class_rw_t
这个类,也就是说
class_rw_t
是运行时产生的;
iva_t
&& method_t
&& protocol_t
class_rw_t
与 class_ro_t
两个类最要的三个成员变量:实例方法列表、实例协议列表、实例变量列表。
这三个列表都是三个基本类型的列表:
ivar_t
表示的是一个实例变量,其关键字段是字段起始位置的相对地址
、字段名称
、字段类型
:1 2 3 4 5 6 7 8 9 10 11 12 13
struct ivar_t { int32_t *offset; const char *name; const char *type; // alignment is sometimes -1; use alignment() instead uint32_t alignment_raw; uint32_t size; uint32_t alignment() const { if (alignment_raw == ~(uint32_t)0) return 1U << WORD_SHIFT; return 1 << alignment_raw; } };
method_t
是表示一个实例方法,其关键字段是方法选择器
、方法名称
、方法实现
三个:1 2 3 4 5 6 7 8 9 10 11 12 13 14
struct method_t { SEL name; const char *types; IMP imp; struct SortBySELAddress : public std::binary_function<const method_t&, const method_t&, bool> { bool operator() (const method_t& lhs, const method_t& rhs) { return lhs.name < rhs.name; } }; };
其中:
IMP
的底层,实际上就是一个函数的地址,编译结果是void *
类型;binary_function
是 c++ 的语言:binary_function
protocol_t
是一个协议的类型,它的结构就比较复杂了:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
struct protocol_t : objc_object { const char *mangledName; struct protocol_list_t *protocols; method_list_t *instanceMethods; method_list_t *classMethods; method_list_t *optionalInstanceMethods; method_list_t *optionalClassMethods; property_list_t *instanceProperties; uint32_t size; // sizeof(protocol_t) uint32_t flags; // Fields below this point are not always present on disk. const char **extendedMethodTypes; const char *_demangledName; const char *demangledName(); const char *nameForLogging() { return demangledName(); } bool isFixedUp() const; void setFixedUp(); bool hasExtendedMethodTypesField() const { return size >= (offsetof(protocol_t, extendedMethodTypes) + sizeof(extendedMethodTypes)); } bool hasExtendedMethodTypes() const { return hasExtendedMethodTypesField() && extendedMethodTypes; } };
总结:类派生图
上面列举的这些类,它们在代码中体现的派生结构,可以用下面的图表示: