iOS底层原理之类的原理分析一

689 阅读13分钟

目录:

1:isa指向与元类的superclass指向分析

书接上文OC底层原理初探之对象的本质(三)alloc探索下继续对类的原理进行探索。

1.1 isa掩码(ISA_MASK)介绍

上文OC底层原理初探之对象的本质(三)alloc探索下中已经介绍过ISA_MASK及其使用方法,接下来我们的探索需要借助ISA_MASK,现行源码版本一共分为三种架构:

x86_64: define ISA_MASK                     0x00007ffffffffff8ULL // ULL: unsigned long long 无符号长整形
arm64: define ISA_MASK                      0x0000000ffffffff8ULL
arm64(simulators): define ISA_MASK          0x007ffffffffffff8ULL

1.2 isa指向分析

代码示例:

#import <Foundation/Foundation.h>
#import "XJPerson.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        XJPerson *p = [XJPerson alloc];
        NSLog(@"%@",p);
    }
    return 0;
}

********************** lldb调试结果 **********************

(lldb) x/4gx p                                     // 16进制格式化输出对象p的内存信息
0x10044f160: 0x001d800100008365 0x0000000000000000 // 得到对象p的isa
0x10044f170: 0x0000000000000000 0x0000000000000000
(lldb) p/x 0x001d800100008365 & 0x00007ffffffffff8 // 用前文的方式,isa & ISA_MASK
(long) $1 = 0x0000000100008360                     // 得到类的地址信息
(lldb) po 0x0000000100008360                       // po 输出
XJPerson                                           // 得到XJPerson

// 继续探索
(lldb) x/4gx 0x0000000100008360                    // 16进制格式化输出XJPerson类的内存信息
0x100008360: 0x0000000100008338 0x00007fff932d8118 // 得到XJPerson类的isa
0x100008370: 0x00007fff6bbb6140 0x0000802c00000000
(lldb) p/x 0x0000000100008338 & 0x00007ffffffffff8 // isa & ISA_MASK
(long) $3 = 0x0000000100008338                     // 得到一个新的地址
(lldb) po 0x0000000100008338                       // po 输出
XJPerson                                           // 还是可以得到XJPerson

很显然,两次输出XJPerson的地址不一样,分别为0x00000001000083600x0000000100008338,这里不禁产生了一个疑问,难道类在运行时会类似对象一样创建多份吗?接下来验证一下:

#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import "XJPerson.h"

//MARK: - 分析类对象内存存在个数
void xjTestClassNum(void){
    Class class1 = [XJPerson class];
    Class class2 = [XJPerson alloc].class;
    Class class3 = object_getClass([XJPerson alloc]);
    Class class4 = [XJPerson alloc].class;
    NSLog(@"\n%p-\n%p-\n%p-\n%p",class1,class2,class3,class4);
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        xjTestClassNum();
    }
    return 0;
}

********************** 打印输出结果 **********************

2021-06-19 15:39:55.257537+0800 002-isa分析[5557:148166] 
0x100008360-
0x100008360-
0x100008360-
0x100008360
Program ended with exit code: 0

从上面案例可以看出,不管生成多少个对象,类始终只有一个,那么0x0000000100008338这个地址po输出为什么也是XJPerson呢?下面用MachOView读取运行文件看看究竟:

image.png

MachOView搜索class除了能看到_OBJC_CLASS_$_XJPerson外,还能看到_OBJC_METACLASS_$_XJPerson,这就是元类(MetaClass)。我们代码中并没有主动生成元类,说明这是程序在运行时主动帮我们生成的。

到这里已经知道对象的isa->类对象,类对象的isa->元类(MetaClass),继续深入探索:

// 省略一些之前代码,并添加XJTeacher类的实例对象的创建代码,XJTeacher继承自XJPerson

// 实例化XJTeacher类的对象
XJTeacher *t = [XJTeacher alloc];
NSLog(@"%@",t);

********************** lldb调试结果 **********************

// XJPerson lldb 调试

// 省略一些之前XJPerson类lldb调试内容

(lldb) po 0x0000000100008338                          // XJPerson类的元类
XJPerson

(lldb) x/4gx 0x0000000100008338                       // 16进制格式化输出XJPerson元类的内存信息
0x100008338: 0x00007fff932d80f0 0x00007fff932d80f0    // 得到XJPerson元类的isa
0x100008348: 0x0000000100406c50 0x0003e03500000007
(lldb) p/x 0x00007fff932d80f0 & 0x00007ffffffffff8    // isa & ISA_MASK
(long) $5 = 0x00007fff932d80f0                        // 得到新的地址
(lldb) po 0x00007fff932d80f0                          // po 输出
NSObject                                              // 输出为NSObject,那么这是类还是元类呢

// XJTeacher lldb 调试

(lldb) x/4gx t                                        // 16进制格式化输出对象t的内存信息
0x100522330: 0x001d800100008315 0x0000000000000000    // 得到对象t的isa
0x100522340: 0x0000000000000000 0x0000000000000000
(lldb) p/x 0x001d800100008315 & 0x00007ffffffffff8    // isa & ISA_MASK
(long) $1 = 0x0000000100008310                        // 得到新的地址
(lldb) po 0x0000000100008310                          // po 输出
XJTeacher                                             // 输出为XJTeacher,即类XJTeacher

(lldb) x/4gx 0x0000000100008310                       // 16进制格式化输出类XJTeacher的内存信息
0x100008310: 0x00000001000082e8 0x0000000100008360    // 得到类XJTeacher的isa
0x100008320: 0x00007fff6bbb6140 0x0000802c00000000
(lldb) p/x 0x00000001000082e8 & 0x00007ffffffffff8    // isa & ISA_MASK
(long) $3 = 0x00000001000082e8                        // 得到新的地址
(lldb) po 0x00000001000082e8                          // po 输出
XJTeacher                                             // 输出为XJTeacher,即元类XJTeacher

(lldb) x/4gx 0x00000001000082e8                       // 16进制格式化输出元类XJTeacher的内存信息
0x1000082e8: 0x00007fff932d80f0 0x0000000100008338    // 得到元类XJTeacher的isa
0x1000082f8: 0x00000001006433a0 0x0003e03500000007
(lldb) p/x 0x00007fff932d80f0 & 0x00007ffffffffff8    // isa & ISA_MASK
(long) $5 = 0x00007fff932d80f0                        
(lldb) po 0x00007fff932d80f0                          
NSObject                                              // 输出为NSObject,那么这是类还是元类呢

// NSObject lldb 调试
// 下面验证一下

(lldb) p/x [NSObject class]                           // 16进制输出NSObject的类的内存信息
(Class) $7 = 0x00007fff932d8118 NSObject              // 得到根类NSObject的地址
(lldb) x/4gx 0x00007fff932d8118                       // 16进制格式化输出根类NSObject的内存信息
0x7fff932d8118: 0x00007fff932d80f0 0x0000000000000000 // 得到根类NSObject的isa
0x7fff932d8128: 0x00000001004992d0 0x0001801000000003
(lldb) p/x 0x00007fff932d80f0 & 0x00007ffffffffff8    // isa & ISA_MASK
(long) $8 = 0x00007fff932d80f0                        // 得到新的地址
(lldb) po 0x00007fff932d80f0                          // po 输出
NSObject                                              // 输出为NSObject,根类的元类,即根元类(RootMetaClass)

// 继续验证
(lldb) x/4gx 0x00007fff932d80f0
0x7fff932d80f0: 0x00007fff932d80f0 0x00007fff932d8118
0x7fff932d8100: 0x0000000100643750 0x0005e03100000007
(lldb) p/x 0x00007fff932d80f0 & 0x00007ffffffffff8
(long) $18 = 0x00007fff932d80f0
(lldb) po 0x00007fff932d80f0
NSObject

图解:

XJPerson的isa走位图

XJTeacher的isa走位图

NSObject的isa走位图1

NSObject的isa走位图2

结论:

  1. 非根类NSObject类的实例对象的isa指向类(Class),类的isa指向元类(MetaClass),元类的isa指向根元类(RootMetaClass),而不是父类的元类。
  2. 根类NSObject类的实例对象的isa指向根类(RootClass),根类的isa指向根元类(RootMetaClass)。
  3. 根元类的isa还是指向根元类。

1.3 元类superclass指向分析

代码实例:

#pragma mark - 元类的superclass链
void xjSuperclass(void) {
    
    //NSObjcet实例对象
    NSObject *objectInstance = [NSObject alloc];
    //NSObject类
    Class object = object_getClass(objectInstance);
    //NSobject元类
    Class metaClass = object_getClass(object);
    //NSObjct根元类
    Class rootMetaClass = object_getClass(metaClass);
    
    NSLog(@"\n%p NSObject实例对象\n%p NSObject类\n%p NSObject元类\n%p NSObject根元类\n",objectInstance,object,metaClass,rootMetaClass);
    
    // XJPerson的元类
    Class personMetaClass = object_getClass(XJPerson.class);
    NSLog(@"XJPerson的元类 : %@ - %p",personMetaClass, personMetaClass);
    
    // XJPerson元类的父类
    Class personSuperMetaclass = class_getSuperclass(personMetaClass);
    NSLog(@"XJPerson元类的父类 : %@ - %p",personSuperMetaclass, personSuperMetaclass);
    
    // XJTeacher的元类
    Class teacherMetaClass = object_getClass(XJTeacher.class);
    NSLog(@"XJTeacher的元类 : %@ - %p",teacherMetaClass,teacherMetaClass);
    
    // XJTeacher元类的父类
    Class childrenSuperMetaClass = class_getSuperclass(teacherMetaClass);
    NSLog(@"XJTeacher元类的父类 : %@ - %p",childrenSuperMetaClass,childrenSuperMetaClass);
    
    // NSObject类的父类
    Class superObject = class_getSuperclass(NSObject.class);
    NSLog(@"NSObject的父类 : %@ - %p",superObject,superObject);
    
    //NSObject根元类的父类
    Class rootSuperMetaObject = class_getSuperclass(rootMetaClass);
    NSLog(@"NSObject根元类的父类 : %@ - %p",rootSuperMetaObject,rootSuperMetaObject);
}

********************** 打印输出结果 **********************

2021-06-19 23:10:35.443934+0800 002-isa分析[12886:361372] 
0x100644850 NSObject实例对象
0x7fff932d8118 NSObject0x7fff932d80f0 NSObject元类
0x7fff932d80f0 NSObject根元类

2021-06-19 23:10:35.444578+0800 002-isa分析[12886:361372] XJPerson的元类 : XJPerson - 0x100008338
2021-06-19 23:10:35.444650+0800 002-isa分析[12886:361372] XJPerson元类的父类 : NSObject - 0x7fff932d80f0
2021-06-19 23:10:35.444800+0800 002-isa分析[12886:361372] XJTeacher的元类 : XJTeacher - 0x1000082e8
2021-06-19 23:10:35.444838+0800 002-isa分析[12886:361372] XJTeacher元类的父类 : XJPerson - 0x100008338
2021-06-19 23:10:35.444908+0800 002-isa分析[12886:361372] NSObject的父类 : (null) - 0x0
2021-06-19 23:10:35.444942+0800 002-isa分析[12886:361372] NSObject根元类的父类 : NSObject - 0x7fff932d8118

结论:

  1. 元类之间也存在继承链,与类一样。
  2. 根元类的父类是根类。
  3. 根类的父类是nil。

图解:

isa流程图.png

2:类的结构分析

本小节将对类的结构进行分析,前面探索过类在底层实现就是objc_class结构体,源码里搜索struct objc_class,会发现在objc4-818.2源码里有三处定义:

2.1 ObjC 1.0的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;

2.2 ObjC 2.0的objc_class(基于objc4-818.2

2.2.1 objc-runtime-old.h文件里objc_class的定义

struct objc_class : objc_object {
    Class superclass;
    const char *name;
    uint32_t version;
    uint32_t info;
    uint32_t instance_size;
    struct old_ivar_list *ivars;
    struct old_method_list **methodLists;
    Cache cache;
    struct old_protocol_list *protocols;
    // CLS_EXT only
    const uint8_t *ivar_layout;
    struct old_class_ext *ext;
    
    // 篇幅原因,省略大部分代码
}

2.2.2 objc-runtime-new.h文件里objc_class的定义

// 现行版本所使用的,我们的探索也是基于这个定义

struct objc_class : objc_object {
  objc_class(const objc_class&) = delete;
  objc_class(objc_class&&) = delete;
  void operator=(const objc_class&) = delete;
  void operator=(objc_class&&) = delete;
    // Class ISA;              // 8字节,隐藏变量isa,继承自objc_object
    Class superclass;          // 8字节
    cache_t cache;             // 16字节,formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
    
    // 篇幅原因,省略大部分代码
}

图解:

image.png

objc-runtime-new.h文件里objc_class的定义可以看出一共有4个成员变量,分别为isa(继承自objc_object的隐藏变量)、superclasscachebits,其中isasuperclass已经分析过了,这里不在赘述,查看cachebits的注释和数据类型定义,终于在struct class_rw_t里发现了方法、属性和协议的相关信息,接下来我们就对bits进行相关探索、验证。

image.png

2.3 objc_class成员变量bits的分析

案例源码:

@interface XJPerson : NSObject
{
    NSString *_age;
    CGFloat   _height;
}

@property (nonatomic, copy) NSString *name;

@property (nonatomic, copy) NSString *hobby;

- (void)sayNB;

+ (void)say666;

+ (void)heiha;

@end

已知类名,想要拿到objc_class的成员变量bits,需要通过内存平移的方式,下面就先简单介绍下内存平移。

2.3.1 内存平移

示例:

image.png

图解:

内存平移1.jpeg

内存平移2.jpeg

结论:知道一个数据结构的首地址后,可以通过内存平移的方式得到其中元素的信息。

2.3.2 类的结构内存计算

想要拿到bits里的数据就得先从首地址平移到bits相应的位置,已知isa8字节,superclass8字节,现在只需算出cache的内存大小就可以了,cache的数据结构为cache_t的结构体,查看其定义(方法存在方法区,static修饰的变量存在全局区,不占用结构体内存空间),发现cache_t结构体占用内存大小为16字节。

// uintptr_t的重命名
typedef unsigned long           uintptr_t;                      // 8字节

// mask_t的重命名,当前为64位环境,所以为4字节
#if __LP64__
typedef uint32_t mask_t;  // x86_64 & arm64 asm are less efficient with 16-bits
#else
typedef uint16_t mask_t;
#endif

struct cache_t { // 经过计算得出cache_t结构体占用内存为16字节
private:
    explicit_atomic<uintptr_t> _bucketsAndMaybeMask;            // 8字节
    union { // 联合体,里面的结构体和_originalPreoptCache互斥,实际上他们都是8字节
        struct {
            explicit_atomic<mask_t>    _maybeMask;              // uint32_t  4字节
#if __LP64__
            uint16_t                   _flags;                  // 2字节
#endif
            uint16_t                   _occupied;               // 2字节
        };                                                      // 结构体一共8字节
        explicit_atomic<preopt_cache_t *> _originalPreoptCache; // 8字节
    };
    
    // 篇幅原因,省略大部分代码
    
    // 下面都是static修饰的变量和方法,不占用结构体内存空间
}

图解:

image.png

2.3.3 获取类的属性

属性获取流程:XJPerson.class -> class_data_bits_t -> class_rw_t -> property_array_t -> property_list_t -> property_t

lldb调试流程:

(lldb) p/x XJPerson.class
// 拿到XJPerson类对象的地址
(Class) $0 = 0x0000000100008380 XJPerson           
(lldb) x/4gx 0x0000000100008380
// 拿到XJPerson类对象的首地址
0x100008380: 0x00000001000083a8 0x0000000100357140 
0x100008390: 0x000000010104e040 0x0002802800000003
// 首地址平移32位
(lldb) p/x 0x100008380 + 0x20    
// 拿到class_data_bits_t bits的地址
(long) $1 = 0x00000001000083a0
// 类型装换
(lldb) p (class_data_bits_t *)0x00000001000083a0   
(class_data_bits_t *) $2 = 0x00000001000083a0
// 指针访问成员变量、方法用`->`,结构体用`.`。
// 使用class_data_bits_t结构体里面的data()函数取出class_rw_t *
(lldb) p $2->data()                               
(class_rw_t *) $3 = 0x000000010104e000
// 使用class_rw_t结构体里面的properties()函数取出property_array_t
(lldb) p $3->properties()                          
(const property_array_t) $4 = {
  list_array_tt<property_t, property_list_t, RawPtr> = {
     = {
      list = {
        ptr = 0x0000000100008260
      }
      arrayAndFlag = 4295000672
    }
  }
}
// 取出list
(lldb) p $4.list                                        
(const RawPtr<property_list_t>) $5 = {
  ptr = 0x0000000100008260
}
// 取出ptr,得到property_list_t指针
(lldb) p $5.ptr
(property_list_t *const) $6 = 0x0000000100008260
// *号取值
(lldb) p *$6
// 得到property_list_t结构体实例,count为2
(property_list_t) $7 = {
  entsize_list_tt<property_t, property_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 16, count = 2)
}
// 读取属性值
(lldb) p $7.get(0)
(property_t) $8 = (name = "name", attributes = "T@\"NSString\",C,N,V_name")
(lldb) p $7.get(1)
(property_t) $9 = (name = "hobby", attributes = "T@\"NSString\",C,N,V_hobby")
(lldb) 

图解:

image.png

2.3.4 获取类的实例方法

实例方法与属性一样,都需要获取到class_rw_t,然后调用methods()函数获取到到方法列表,最后获取到method_t,但是method_t结构体里面没有成员变量,所以直接输出会显示为空,需要获取method_t里面的big结构体。

实例方法获取流程:XJPerson.class -> class_data_bits_t -> class_rw_t -> method_array_t -> method_list_t -> method_t->bigbig不行就用small)。

method_t部分源码:

// 源码来着objc4-818.2->objc-runtime-new.h->726-861

struct method_t {
    static const uint32_t smallMethodListFlag = 0x80000000;

    method_t(const method_t &other) = delete;

    // The representation of a "big" method. This is the traditional
    // representation of three pointers storing the selector, types
    // and implementation.
    struct big {
        SEL name;
        const char *types;
        MethodListIMP imp;
    };
    
    // 篇幅原因,省略大部分代码
}

lldb调试流程:

(lldb) p/x XJPerson.class
// 拿到XJPerson类对象的地址
(Class) $0 = 0x0000000100008380 XJPerson
(lldb) x/4gx 0x0000000100008380
// 拿到XJPerson类对象的首地址
0x100008380: 0x00000001000083a8 0x0000000100357140
0x100008390: 0x0000000100682630 0x0002802800000003
// 首地址平移32位
(lldb) p/x 0x100008380 + 0x20
// 拿到class_data_bits_t bits的地址
(long) $1 = 0x00000001000083a0
// 类型装换
(lldb) p/x (class_data_bits_t *)0x00000001000083a0
(class_data_bits_t *) $2 = 0x00000001000083a0
// 使用class_data_bits_t结构体里面的data()函数取出class_rw_t *
(lldb) p $2->data()
(class_rw_t *) $3 = 0x00000001006825f0
// 使用class_rw_t结构体里面的methods()函数取出method_array_t
(lldb) p $3->methods()
(const method_array_t) $4 = {
  list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
     = {
      list = {
        ptr = 0x0000000100008160
      }
      arrayAndFlag = 4295000416
    }
  }
}
// 取出list
(lldb) p $4.list
(const method_list_t_authed_ptr<method_list_t>) $5 = {
  ptr = 0x0000000100008160
}
// 取出ptr,得到method_list_t指针
(lldb) p $5.ptr
(method_list_t *const) $6 = 0x0000000100008160
// *号取值
(lldb) p *$6
// 得到method_list_t结构体实例,count为6
(method_list_t) $7 = {
  entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 6)
}
// 通过get()函数拿到method_t,然后通过big()函数读出实例方法信息
(lldb) p $7.get(0).big()
(method_t::big) $8 = {
  name = "sayNB"
  types = 0x0000000100003f7a "v16@0:8"
  imp = 0x0000000100003d40 (KCObjcBuild`-[XJPerson sayNB])
}
(lldb) p $7.get(1).big()
(method_t::big) $9 = {
  name = "hobby"
  types = 0x0000000100003f72 "@16@0:8"
  imp = 0x0000000100003db0 (KCObjcBuild`-[XJPerson hobby])
}
(lldb) p $7.get(2).big()
(method_t::big) $10 = {
  name = "setHobby:"
  types = 0x0000000100003f8e "v24@0:8@16"
  imp = 0x0000000100003de0 (KCObjcBuild`-[XJPerson setHobby:])
}
(lldb) p $7.get(3).big()
(method_t::big) $11 = {
  name = "init"
  types = 0x0000000100003f72 "@16@0:8"
  imp = 0x0000000100003ce0 (KCObjcBuild`-[XJPerson init])
}
(lldb) p $7.get(4).big()
(method_t::big) $12 = {
  name = "name"
  types = 0x0000000100003f72 "@16@0:8"
  imp = 0x0000000100003d50 (KCObjcBuild`-[XJPerson name])
}
(lldb) p $7.get(5).big()
(method_t::big) $13 = {
  name = "setName:"
  types = 0x0000000100003f8e "v24@0:8@16"
  imp = 0x0000000100003d80 (KCObjcBuild`-[XJPerson setName:])
}
(lldb) 

图解:

image.png

2.3.5 获取类的成员变量(ivars)

成员变量(ivars)获取流程:XJPerson.class -> class_data_bits_t -> class_rw_t -> class_ro_t -> ivar_list_t -> ivar_t

成员变量(ivars)与属性不同,存在class_ro_t结构体里。

class_ro_t部分源码:

// 源码来自objc4-818.2->objc-runtime-new.h->1037-1171
struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
#ifdef __LP64__
    uint32_t reserved;
#endif

    union {
        const uint8_t * ivarLayout;
        Class nonMetaclass;
    };

    explicit_atomic<const char *> name;
    // With ptrauth, this is signed if it points to a small list, but
    // may be unsigned if it points to a big list.
    void *baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars; // 成员变量

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;
    
    
    // 篇幅原因,省略大部分代码
}

// 源码来自objc4-818.2->objc-runtime-new.h->863-883
struct ivar_t {
#if __x86_64__
    // *offset was originally 64-bit on some x86_64 platforms.
    // We read and write only 32 bits of it.
    // Some metadata provides all 64 bits. This is harmless for unsigned 
    // little-endian values.
    // Some code uses all 64 bits. class_addIvar() over-allocates the 
    // offset for their benefit.
#endif
    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;
    }
};

lldb调试流程:

// 省略获取class_rw_t的流程
(class_rw_t *) $3 = 0x00000001007cfe30
// 调用class_rw_t的ro()函数获取class_ro_t
(lldb) p $3->ro()
(const class_ro_t *) $4 = 0x0000000100008110
// 取值
(lldb) p *$4
// 得到class_ro_t结构体
(const class_ro_t) $5 = {
  flags = 0
  instanceStart = 8
  instanceSize = 40
  reserved = 0
   = {
    ivarLayout = 0x0000000000000000
    nonMetaclass = nil
  }
  name = {
    std::__1::atomic<const char *> = "XJPerson" {
      Value = 0x0000000100003e96 "XJPerson"
    }
  }
  baseMethodList = 0x0000000100003de0
  baseProtocols = nil
  ivars = 0x0000000100008158 // 成员变量
  weakIvarLayout = 0x0000000000000000
  baseProperties = 0x00000001000081e0
  _swiftMetadataInitializer_NEVER_USE = {}
}
// `.`语法取出成员变量列表
(lldb) p $5.ivars
// 获得ivar_list_t指针
(const ivar_list_t *const) $6 = 0x0000000100008158
// 取值
(lldb) p *$6
// 获得ivar_list_t,count为4
(const ivar_list_t) $7 = {
  entsize_list_tt<ivar_t, ivar_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 32, count = 4)
}
// 通过get()函数拿到ivar_t
(lldb) p $7.get(0)
(ivar_t) $8 = {
  offset = 0x00000001000082bc
  name = 0x0000000100003f10 "_age"
  type = 0x0000000100003f55 "@\"NSString\""
  alignment_raw = 3
  size = 8
}
(lldb) p $7.get(1)
(ivar_t) $9 = {
  offset = 0x00000001000082c0
  name = 0x0000000100003f15 "_height"
  type = 0x0000000100003f7c "d"
  alignment_raw = 3
  size = 8
}
(lldb) p $7.get(2)
(ivar_t) $10 = {
  offset = 0x00000001000082c4
  name = 0x0000000100003f1d "_name"
  type = 0x0000000100003f55 "@\"NSString\""
  alignment_raw = 3
  size = 8
}
(lldb) p $7.get(3)
(ivar_t) $11 = {
  offset = 0x00000001000082c8
  name = 0x0000000100003f23 "_hobby"
  type = 0x0000000100003f55 "@\"NSString\""
  alignment_raw = 3
  size = 8
}

图解:

image.png

2.3.6 获取类的类方法

类方法存在元类中,所以需要先获取元类,然后获取元类的方法列表,才能获取到类方法。

类方法获取流程:XJPerson.class -> Metaclass -> class_data_bits_t -> class_rw_t -> method_array_t -> method_list_t -> method_t->bigbig不行就用small)。

lldb调试流程:

// M1 iMac调试
(lldb) p/x XJPerson.class
(Class) $0 = 0x0000000100008328 XJPerson
(lldb) x/4gx 0x0000000100008328
0x100008328: 0x0000000100008350 0x0000000100379140
0x100008338: 0x000000010036d0f0 0x0000803000000000
(lldb) p/x 0x0000000100008350 & 0x0000000ffffffff8
(long) $1 = 0x0000000100008350
(lldb) x/4gx 0x0000000100008350
0x100008350: 0x00000001003790f0 0x00000001003790f0
0x100008360: 0x00010001009065f0 0x0002e03400000000
(lldb) p/x 0x100008350 + 0x20
(long) $2 = 0x0000000100008370
(lldb) p/x (class_data_bits_t *)0x0000000100008370
(class_data_bits_t *) $3 = 0x0000000100008370
(lldb) p $3->data()
(class_rw_t *) $4 = 0x0000000100a10e10
(lldb) p $4->methods()
(const method_array_t) $5 = {
  list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
     = {
      list = {
        ptr = 0x0000000100003db8
      }
      arrayAndFlag = 4294983096
    }
  }
}
(lldb) p $5.list
(const method_list_t_authed_ptr<method_list_t>) $6 = {
  ptr = 0x0000000100003db8
}
(lldb) p $6.ptr
(method_list_t *const) $7 = 0x0000000100003db8
(lldb) p *$7
(method_list_t) $8 = {
  entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 2147483660, count = 2)
}
(lldb) p $8.get(0).small()
(method_t::small) $9 = {
  name = (offset = 17608)
  types = (offset = 419)
  imp = (offset = -272)
}
(lldb) p $8.get(1).small()
(method_t::small) $10 = {
  name = (offset = 17604)
  types = (offset = 407)
  imp = (offset = -264)
}
(lldb) p $8.get(0).getDescription()
(objc_method_description *) $9 = 0x0000000100717a80
(lldb) p *$9
(objc_method_description) $10 = (name = "say666", types = "v16@0:8")
(lldb) p *($8.get(1).getDescription())
(objc_method_description) $11 = (name = "heiha", types = "v16@0:8")

图解:

image.png image.png

结论:类是元类的类对象,且具有唯一性,类方法即是元类的实例方法。

2.3.7 获取类的协议

想要获取类的协议(protocol),必要条件是需要先遵守协议。

协议获取流程:XJPerson.class -> class_data_bits_t -> class_rw_t -> protocol_array_t -> protocol_list_t -> protocol_ref_t -> protocol_t -> method_list_t -> method_t -> bigbig不行就用small)。

protocol_list_tprotocol_ref_tprotocol_t部分源码:

image.png image.png image.png

测试代码示例:

image.png

image.png

lldb调试流程:

(lldb) p/x XJPerson.class
(Class) $0 = 0x0000000100008778 XJPerson
(lldb) p/x 0x0000000100008778 + 0x20
(long) $1 = 0x0000000100008798
(lldb) p (class_data_bits_t *)0x0000000100008798
(class_data_bits_t *) $2 = 0x0000000100008798
(lldb) p $2->data()
(class_rw_t *) $3 = 0x00000001009c5d90
(lldb) p $3->protocols()
(const protocol_array_t) $4 = {
  list_array_tt<unsigned long, protocol_list_t, RawPtr> = {
     = {
      list = {
        ptr = 0x0000000100008168
      }
      arrayAndFlag = 4295000424
    }
  }
}
(lldb) p $4.list
(const RawPtr<protocol_list_t>) $5 = {
  ptr = 0x0000000100008168
}
(lldb) p $5.ptr
(protocol_list_t *const) $6 = 0x0000000100008168
(lldb) p *$6
(protocol_list_t) $7 = (count = 2, list = protocol_ref_t [] @ 0x00000001196abc58)
(lldb) p $7.list[0]
(protocol_ref_t) $8 = 4295002064
(lldb) p (protocol_t *)4295002064
(protocol_t *) $9 = 0x00000001000087d0
p $9[0]
(protocol_t) $10 = {
  objc_object = {
    isa = {
      bits = 4298608840
      cls = Protocol
       = {
        nonpointer = 0
        has_assoc = 0
        has_cxx_dtor = 0
        shiftcls = 537326105
        magic = 0
        weakly_referenced = 0
        unused = 0
        has_sidetable_rc = 0
        extra_rc = 0
      }
    }
  }
  mangledName = 0x0000000100003e52 "XJPersonDelegate"
  protocols = 0x00000001000085b0
  instanceMethods = 0x00000001000085c8
  classMethods = nil
  optionalInstanceMethods = nil
  optionalClassMethods = nil
  instanceProperties = nil
  size = 96
  flags = 0
  _extendedMethodTypes = 0x0000000100008600
  _demangledName = 0x0000000000000000
  _classProperties = nil
}
(lldb) p $10.instanceMethods
(method_list_t *) $11 = 0x00000001000085c8
(lldb) p $11->get(0).big()
(method_t::big) $12 = {
  name = "delegateMethod1"
  types = 0x0000000100003e77 "v16@0:8"
  imp = 0x0000000000000000
}
(lldb) p $11->get(1).big()
(method_t::big) $13 = {
  name = "delegateMethod2:"
  types = 0x0000000100003ea9 "@24@0:8@16"
  imp = 0x0000000000000000
}
(lldb) p $9[1]
(protocol_t) $14 = {
  objc_object = {
    isa = {
      bits = 4298608840
      cls = Protocol
       = {
        nonpointer = 0
        has_assoc = 0
        has_cxx_dtor = 0
        shiftcls = 537326105
        magic = 0
        weakly_referenced = 0
        unused = 0
        has_sidetable_rc = 0
        extra_rc = 0
      }
    }
  }
  mangledName = 0x0000000100003e36 "XJPersonDataSource"
  protocols = 0x0000000100008278
  instanceMethods = 0x0000000100008290
  classMethods = nil
  optionalInstanceMethods = nil
  optionalClassMethods = nil
  instanceProperties = nil
  size = 96
  flags = 0
  _extendedMethodTypes = 0x00000001000082c8
  _demangledName = 0x0000000000000000
  _classProperties = nil
}
(lldb) p $14.instanceMethods
(method_list_t *) $15 = 0x0000000100008290
(lldb) p $15->get(0).big()
(method_t::big) $16 = {
  name = "dataSourceMethod1"
  types = 0x0000000100003e77 "v16@0:8"
  imp = 0x0000000000000000
}
(lldb) p $15->get(1).big()
(method_t::big) $17 = {
  name = "dataSourceMethod2:"
  types = 0x0000000100003ea9 "@24@0:8@16"
  imp = 0x0000000000000000
}

图解:

image.png image.png