对小码哥底层班视频学习的总结与记录. 面试题部分, 通过对面试题的分析探索问题的本质内容. 上接 iOS 底层原理总结 - 探寻 OC 对象的本质
Class 的本质
我们知道不管是类对象还是元类对象, 类型都是 Class,class 和 mete-class 的底层都是 objc_class 结构体的指针, 内存中就是结构体, 本章来探寻 Class 的本质.
- Class objectClass = [NSObject class];
- Class objectMetaClass = object_getClass([NSObject class]);
点击 Class 来到内部, 我们可以发现
typedef struct objc_class *Class;
Class 对象其实是一个指向 objc_class 结构体的指针. 因此我们可以说类对象或元类对象在内存中其实就是 objc_class 结构体.
我们来到 objc_class 内部, 可以看到这段在底层原理中经常出现的代码.
- struct objc_class {
- Class _Nonnull isa OBJC_ISA_AVAILABILITY;
- #if !__OBJC2__
- Class _Nullable super_class OBJC2_UNAVAILABLE;
- const char * _Nonnull name OBJC2_UNAVAILABLE;
- long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
- #endif
- } OBJC2_UNAVAILABLE;
- /* Use `Class` instead of `struct objc_class *` */
这部分代码相信在文章中很常见, 但是 OBJC2_UNAVAILABLE; 说明这些代码已经不在使用了. 那么目前 objc_class 的结构是什么样的呢? 我们通过 objc 源码中去查找 objc_class 结构体的内容.
我们发现这个结构体继承 objc_object 并且结构体内有一些函数, 因为这是 c++ 结构体, 在 c 上做了扩展, 因此结构体中可以包含函数. 我们来到 objc_object 内, 截取部分代码
我们发现 objc_object 中有一个 isa 指针, 那么 objc_class 继承 objc_object, 也就同样拥有一个 isa 指针
那么我们之前了解到的, 类中存储的类的成员变量信息, 实例方法, 属性名等这些信息在哪里呢. 我们来到 class_rw_t 中, 截取部分代码, 我们发现 class_rw_t 中存储着方法列表, 属性列表, 协议列表等内容.
而 class_rw_t 是通过 bits 调用 data 方法得来的, 我们来到 data 方法内部实现. 我们可以看到, data 函数内部仅仅对 bits 进行 & FAST_DATA_MASK 操作
而成员变量信息则是存储在 class_ro_t 内部中的, 我们来到 class_ro_t 内查看.
最后总结通过一张图进行总结
如何证明上述内容是正确的.
我们可以自定义一个结构体, 如果我们自己写的结构和 objc_class 真实结构是一样的, 那么当我们强制转化的时候, 就会一一对应的赋值. 此时我们就可以拿到结构体内部的信息.
下列代码是我们仿照 objc_class 结构体, 提取其中需要使用到的信息, 自定义的一个结构体.
- #import <Foundation/Foundation.h>
- #ifndef XXClassInfo_h
- #define XXClassInfo_h
- # if __arm64__
- # define ISA_MASK 0x0000000ffffffff8ULL
- # elif __x86_64__
- # define ISA_MASK 0x00007ffffffffff8ULL
- # endif
- #if __LP64__
- typedef uint32_t mask_t;
- #else
- typedef uint16_t mask_t;
- #endif
- typedef uintptr_t cache_key_t;
- struct bucket_t {
- cache_key_t _key;
- IMP _imp;
- };
- struct cache_t {
- bucket_t *_buckets;
- mask_t _mask;
- mask_t _occupied;
- };
- struct entsize_list_tt {
- uint32_t entsizeAndFlags;
- uint32_t count;
- };
- struct method_t {
- SEL name;
- const char *types;
- IMP imp;
- };
- struct method_list_t : entsize_list_tt {
- method_t first;
- };
- struct ivar_t {
- int32_t *offset;
- const char *name;
- const char *type;
- uint32_t alignment_raw;
- uint32_t size;
- };
- struct ivar_list_t : entsize_list_tt {
- ivar_t first;
- };
- struct property_t {
- const char *name;
- const char *attributes;
- };
- struct property_list_t : entsize_list_tt {
- property_t first;
- };
- struct chained_property_list {
- chained_property_list *next;
- uint32_t count;
- property_t list[0];
- };
- typedef uintptr_t protocol_ref_t;
- struct protocol_list_t {
- uintptr_t count;
- protocol_ref_t list[0];
- };
- struct class_ro_t {
- uint32_t flags;
- uint32_t instanceStart;
- uint32_t instanceSize; // instance 对象占用的内存空间
- #ifdef __LP64__
- uint32_t reserved;
- #endif
- const uint8_t * ivarLayout;
- const char * name; // 类名
- method_list_t * baseMethodList;
- protocol_list_t * baseProtocols;
- const ivar_list_t * ivars; // 成员变量列表
- const uint8_t * weakIvarLayout;
- property_list_t *baseProperties;
- };
- struct class_rw_t {
- uint32_t flags;
- uint32_t version;
- const class_ro_t *ro;
- method_list_t * methods; // 方法列表
- property_list_t *properties; // 属性列表
- const protocol_list_t * protocols; // 协议列表
- Class firstSubclass;
- Class nextSiblingClass;
- char *demangledName;
- };
- #define FAST_DATA_MASK 0x00007ffffffffff8UL
- struct class_data_bits_t {
- uintptr_t bits;
- public:
- class_rw_t* data() { // 提供 data() 方法进行 & FAST_DATA_MASK 操作
- return (class_rw_t *)(bits & FAST_DATA_MASK);
- }
- };
- /* OC 对象 */
- struct xx_objc_object {
- void *isa;
- };
- /* 类对象 */
- struct xx_objc_class : xx_objc_object {
- Class superclass;
- cache_t cache;
- class_data_bits_t bits;
- public:
- class_rw_t* data() {
- return bits.data();
- }
- xx_objc_class* metaClass() { // 提供 metaClass 函数, 获取元类对象
- // 上一篇我们讲解过, isa 指针需要经过一次 & ISA_MASK 操作之后才得到真正的地址
- return (xx_objc_class *)((long long)isa & ISA_MASK);
- }
- };
- #endif /* XXClassInfo_h */
接下来我们将自己定义的类强制转化为我们自定义的精简的 class 结构体类型.
- #import <Foundation/Foundation.h>
- #import <objc/runtime.h>
- #import "XXClassInfo.h"
- /* Person */
- @interface Person : NSObject <NSCopying>
- {
- @public
- int _age;
- }
- @property (nonatomic, assign) int height;
- - (void)personMethod;
- + (void)personClassMethod;
- @end
- @implementation Person
- - (void)personMethod {}
- + (void)personClassMethod {}
- @end
- /* Student */
- @interface Student : Person <NSCoding>
- {
- @public
- int _no;
- }
- @property (nonatomic, assign) int score;
- - (void)studentMethod;
- + (void)studentClassMethod;
- @end
- @implementation Student
- - (void)studentMethod {}
- + (void)studentClassMethod {}
- @end
- int main(int argc, const char * argv[]) {
- @autoreleasepool {
- NSObject *object = [[NSObject alloc] init];
- Person *person = [[Person alloc] init];
- Student *student = [[Student alloc] init];
- xx_objc_class *objectClass = (__bridge xx_objc_class *)[object class];
- xx_objc_class *personClass = (__bridge xx_objc_class *)[person class];
- xx_objc_class *studentClass = (__bridge xx_objc_class *)[student class];
- xx_objc_class *objectMetaClass = objectClass->metaClass();
- xx_objc_class *personMetaClass = personClass->metaClass();
- xx_objc_class *studentMetaClass = studentClass->metaClass();
- class_rw_t *objectClassData = objectClass->data();
- class_rw_t *personClassData = personClass->data();
- class_rw_t *studentClassData = studentClass->data();
- class_rw_t *objectMetaClassData = objectMetaClass->data();
- class_rw_t *personMetaClassData = personMetaClass->data();
- class_rw_t *studentMetaClassData = studentMetaClass->data();
- // 0x00007ffffffffff8
- NSLog(@"%p %p %p %p %p %p", objectClassData, personClassData, studentClassData,
- objectMetaClassData, personMetaClassData, studentMetaClassData);
- return 0;
- }
通过打断点, 我们可以看到 class 内部信息.
至此, 我们再次拿出那张经典的图, 挨个分析图中 isa 指针和 superclass 指针的指向
instance 对象
首先我们来看 instance 对象, 我们通过上一篇文章知道, instance 对象中存储着 isa 指针和其他成员变量, 并且 instance 对象的 isa 指针是指向其类对象地址的. 我们首先分析上述代码中我们创建的 object,person,student 三个 instance 对象与其相对应的类对象 objectClass,personClass,studentClass.
从上图中我们可以发现 instance 对象中确实存储了 isa 指针和其成员变量, 同时将 instance 对象的 isa 指针经过 & 运算之后计算出的地址确实是其相应类对象的内存地址. 由此我们证明 isa,superclass 指向图中的 1,2,3 号线.
class 对象
接着我们来看 class 对象, 同样通过上一篇文章, 我们明确 class 对象中存储着 isa 指针, superclass 指针, 以及类的属性信息, 类的成员变量信息, 类的对象方法, 和类的协议信息, 而通过上面对 object 源码的分析, 我们知道这些信息存储在 class 对象的 class_rw_t 中, 我们通过强制转化来窥探其中的内容. 如下图
上图中我们通过模拟对 person 类对象调用. data 函数, 即对 bits 进行 & FAST_DATA_MASK(0x00007ffffffffff8UL) 运算, 并转化为 class_rw_t. 即上图中的 personClassData. 其中我们发现成员变量信息, 对象方法, 属性等信息只显示 first 第一个, 如果想要拿到更多的需要通过代码将指针后移获取. 而上图中的 instaceSize = 16 也同 person 对象中 isa 指针 8 个字节 +_age4 个字节 +_height4 个字节相对应起来. 这里不在展开对 objectClassData 及 studentClassData 进行分析, 基本内容同 personClassData 相同.
那么类对象中的 isa 指针和 superclass 指针的指向是否如那张经典的图示呢? 我们来验证一下.
通过上图中的内存地址的分析, 由此我们证明 isa,superclass 指向图中, isa 指针的 4,5,6 号线, 以及 superclass 指针的 10,11,12 号线.
meta-class 对象
最后我们来看 meta-class 元类对象, 上文提到 meta-class 中存储着 isa 指针, superclass 指针, 以及类的类方法信息. 同时我们知道 meta-class 元类对象与 class 类对象, 具有相同的结构, 只不过存储的信息不同, 并且元类对象的 isa 指针指向基类的元类对象, 基类的元类对象的 isa 指针指向自己. 元类对象的 superclass 指针指向其父类的元类对象, 基类的元类对象的 superclass 指针指向其类对象.
与 class 对象相同, 我们同样通过模拟对 person 元类对象调用. data 函数, 即对 bits 进行 & FAST_DATA_MASK(0x00007ffffffffff8UL) 运算, 并转化为 class_rw_t.
首先我们可以看到结构同 personClassData 相同, 并且成员变量及属性列表等信息为空, 而 methods 中存储着类方法 personClassMethod.
接着来验证 isa 及 superclass 指针的指向是否同上图序号标注一样.
上图中通过地址证明 meta-class 的 isa 指向基类的 meta-class, 基类的 isa 指针也指向自己.
上图中通过地址证明 meta-class 的 superclass 指向父类的 meta-class, 基类的 meta-class 的 superclass 指向基类的 class 类.
最近看到一句话, 在这里与大家共勉. 当承认自己与别人的差距的时候, 会变得很轻松. 因为我们终于不用计较为什么他行我不行, 终于不用默默努力较劲心里告诉自己一定要超过别人. 而同时也失去了斗志, 失去了战胜别人得唯一筹码. 最怕一生碌碌无为, 还安慰自己平凡可贵.
来源: https://juejin.im/post/5ad210636fb9a028da7cf90c