OC底层原理04- 类的原理分析上

前言

关于alloc已经探索完了 接下来我们来看一下isa分析到元类

一.isa走位图和继承链

一.lldb调试

先搞一波运行起来demo

        LGPerson *test = [LGPerson alloc];
        断点这里 NSLog(@"%@",test);
复制代码

1.lldb 终端输入如下命令得到实例对象地址

    po test
    <LGPerson: 0x10500de60> 得到实例对象内存地址
复制代码

2.打印内存

x/4gx 0x10500de60
0x10500de60(内存地址): 0x011d800100008365(如何能证明这个是isa) 0x0000000000000000
0x10500de70: 0x0000000000000000 0x0000000000000000
复制代码

3.得到类isa指向的地址

p/x 0x011d800100008365 & 0x00007ffffffffff8
(unsigned long long) $2 = 0x0000000100008360 (获取类的内存地址)
复制代码

4.打印类名称

 po 0x0000000100008360 
LGPerson(得到类名称)
复制代码

到此我们可以探索到实例对象的isa->(LGPerson:0x0000000100008360)类 那么类对象的isa指向哪里呢?我们继续走你

5.打印类的内存

x/4gx 0x0000000100008360
0x100008360: 0x0000000100008338(isa) 0x00007fff88bdfcc8
0x100008370: 0x0000000100641640 0x0002802c00000003
复制代码

6.得到类isa指向的地址

p/x 0x0000000100008338 & 0x00007ffffffffff8
$8 = 0x0000000100008338(获取类的内存地址)
复制代码

7.打印类名称

 po 0x0000000100008338 
LGPerson(得到类名称)
复制代码

到此我们可以探索到(LGPerson:0x0000000100008360)的isa->(LGPerson:0x0000000100008338 (这是什么东东))

8.运行另外一个demo

    Class class1 = [LGPerson class];
    Class class2 = [LGPerson alloc].class;
    Class class3 = object_getClass([LGPerson alloc]);
    Class class4 = [LGPerson alloc].class;
    NSLog(@"\n%p-\n%p-\n%p-\n%p",class1,class2,class3,class4);
    打印结果地址: 0x100008360-
                0x100008360-
                0x100008360-
                0x100008360
   和 LGPerson:0x0000000100008360 对的上
复制代码

9.看一下mach-o文件 LGPerson:0x0000000100008338 应该是元类

Xnip2021-06-17_23-42-48.jpg

到此我们可以探索到(LGPerson:0x0000000100008338)的isa 又指向哪里呢 继续走你

10.打印元类的内存

x/4gx 0x0000000100008338
0x100008338: 0x00007fff88bdfca0 (isa)0x00007fff88bdfca0
0x100008348: 0x00000001006419d0 0x0002e03500000003
复制代码

11.得到元类isa指向的地址

p/x 0x00007fff88bdfca0 & 0x00007ffffffffff8ULL
(unsigned long long) $10 = 0x00007fff88bdfca0(获取类的内存地址)

复制代码

12.打印类名称

po 0x00007fff88bdfca0
NSObject
复制代码

到此我们可以探索到(LGPerson:0x0000000100008338)的isa 指向根元类(NSObject) 那么根元类的isa又指向哪里呢

13.打印根元类的内存

x/4gx 0x00007fff88bdfca0
0x7fff88bdfca0: 0x00007fff88bdfca0 0x00007fff88bdfcc8
0x7fff88bdfcb0: 0x0000000100641b10 0x0003e03100000007
复制代码

14.得到根元类isa指向的地址

 p/x 0x00007fff88bdfca0 & 0x00007ffffffffff8
(unsigned long long) $12 = 0x00007fff88bdfca0(获取类的内存地址)

复制代码

15.打印类名称

po 0x00007fff88bdfca0
NSObject
复制代码

到此我们可以探索到根元类(NSObject)的isa 指向了自己

总结:对象 isa -> 类(LGPerson:0x0000000100008360) isa -> 元类(LGPerson:0x0000000100008338) isa -> 根元类(NSObject) isa -> 根元类

16.我们直接打印NSObject.class

p/x NSObject.class
(Class) $14 = 0x00007fff88bdfcc8 NSObject

打印类的内存
x/4gx 0x00007fff88bdfcc8
0x7fff88bdfcc8: 0x00007fff88bdfca0 0x0000000000000000
0x7fff88bdfcd8: 0x0000000105007bd0 0x0002801000000003

打印类的地址
p/x 0x00007fff88bdfca0 & 0x00007ffffffffff8
(long) $15 = 0x00007fff88bdfca0

打印类的名称
po 0x00007fff88bdfca0
NSObject
复制代码

总结:根类 isa -> 根元类 isa ->根元类

二.代码调试isa走向

1.我们创建的NSObject实例对象演示:

Xnip2021-06-20_10-44-18.jpg

2.我们创建LGPerson实例对象演示:

Xnip2021-06-20_10-48-52.jpg

3.因为LGTeacher继承于LGPersonLGTeacher实例对象演示:

Xnip2021-06-20_10-52-47.jpg

下面我们探索一下继承的isa是怎么样指向的 直接上代码

三.代码调试isa继承走向

一.类继承的isa指向

1.我们先看一下 NSObject.classisa指向

Xnip2021-06-20_11-44-56.jpg

2.我们看一下 LGPerson.classisa指向

Xnip2021-06-20_11-45-24.jpg

3.我们看一下 LGTeacher.classisa指向

Xnip2021-06-20_11-45-44.jpg

4.我们看一下 LGXiaowen.classisa指向

Xnip2021-06-20_11-46-03.jpg

二.元类继承的isa指向

1.我们先看一下 NSObject.class元类的isa指向

Xnip2021-06-20_12-45-03.jpg

2.我们看一下 LGPerson.class元类的isa指向

Xnip2021-06-20_12-45-16.jpg

3.我们看一下 LGTeacher.class元类的isa指向

Xnip2021-06-20_12-45-28.jpg

4.我们看一下 LGXiaowen.class元类的isa指向

Xnip2021-06-20_12-45-50.jpg

结合上面分析得到如下结论如下图

isa流程图.png

二.源码分析类的结构

Xnip2021-06-20_13-28-25.jpg

三.指针和内存平移

  // 数组指针
        int c[4] = {1,2,3,4};
        int *d   = c;
        NSLog(@"%p - %p - %p",&c,&c[0],&c[1]);
        NSLog(@"%p - %p - %p",d,d+1,d+2);

        for (int i = 0; i<4; i++) {
            int value =  *(d+i);
            NSLog(@"%d",value);
        }
        
打印得出:002-内存偏移[38980:1658739] 0x7ffeefbff3c0 - 0x7ffeefbff3c0 - 0x7ffeefbff3c4

2021-06-20 13:41:36.896925+0800 002-内存偏移[38980:1658739] 0x7ffeefbff3c0 - 0x7ffeefbff3c4 - 0x7ffeefbff3c8

2021-06-20 13:41:37.939247+0800 002-内存偏移[38980:1658739] 1
2021-06-20 13:42:19.343718+0800 002-内存偏移[38980:1658739] 2
2021-06-20 13:42:19.343888+0800 002-内存偏移[38980:1658739] 3
2021-06-20 13:42:19.343959+0800 002-内存偏移[38980:1658739] 4

复制代码

根据指针内存的平移获取数组相应的取值 这样我们就可以根据类的首地址进行平台获取里面成员变量的值

四.类的结构内存计算

Xnip2021-06-20_13-51-43.jpg

x/4gx LGPerson.class
0x100008380: 0x00000001000083a8 0x000000010036a140
0x100008390: 0x0000000101052a80 0x0002802800000003
0x00000001000083a8:对应Class ISA
0x000000010036a140:对应Class superclass
0x0000000101052a80:对应cache_t cache
0x0002802800000003:class_data_bits bits

复制代码

想要获取到bits 就的通过首地址平移这些长度ISA(8字节)->superclass(8字节)->cache(多少呢)

Xnip2021-06-20_14-07-44.jpg
cache = 8+8 = 16 所以平移到bits需要移动32

Xnip2021-06-20_14-12-30.jpg

五.lldb分析类的结构

1.在class_rw_t里面查找LGPerson的成员变量

(lldb) x/4gx LGPerson.class
0x100008380: 0x00000001000083a8 0x000000010036a140
0x100008390: 0x0000000100764050 0x0002802800000003
(lldb) p (class_data_bits_t *) 0x1000083a0  //获取class_data_bits_t +32
(class_data_bits_t *) $1 = 0x00000001000083a0
(lldb) p $1->data() //获取 class_rw_t
(class_rw_t *) $2 = 0x0000000100764010
(lldb) p * $2   //查看 class_rw_t
(class_rw_t) $3 = {
  flags = 2148007936
  witness = 1
  ro_or_rw_ext = {
    std::__1::atomic<unsigned long> = {
      Value = 4295000344
    }
  }
  firstSubclass = nil
  nextSiblingClass = NSUUID
  
}

//获取属性
(lldb) p $2.properties() //查看 class_rw_t.properties() 
(const property_array_t) $4 = {
  list_array_tt<property_t, property_list_t, RawPtr> = {
     = {
     `list` = {
        ptr = 0x0000000100008260
      }
      arrayAndFlag = 4295000672
    }
  }
}
  Fix-it applied, fixed expression was: 
    $2->properties()
(lldb) p $4.list //上面返回的
(const RawPtr<property_list_t>) $5 = {
  `ptr` = 0x0000000100008260
}
(lldb) p $5.ptr
(property_list_t *const) $6 = 0x0000000100008260
(lldb) p *$6
(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")

 p $7.get(1)
(property_t) $9 = (name = "hobby", attributes = "T@\"NSString\",C,N,V_hobby")

获取对象实例方法

p $2.methods()  //查看 class_rw_t.methods() 
(const method_array_t) $10 = {
  list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
     = {
      list = {
        ptr = 0x0000000100008160
      }
      arrayAndFlag = 4295000416
    }
  }
}
  Fix-it applied, fixed expression was: 
    $2->methods()
(lldb) p $10.list
(const method_list_t_authed_ptr<method_list_t>) $11 = {
  ptr = 0x0000000100008160
}
(lldb) p $11.ptr
(method_list_t *const) $12 = 0x0000000100008160
(lldb) p *$12
(method_list_t) $13 = {
  entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 6)
}
(lldb) p $13.get(0).big().name
(SEL) $14 = "hobby"
(lldb) p $13.get(1).big().name
(SEL) $15 = "sayNB"
(lldb) p $13.get(2).big().name
(SEL) $16 = "setHobby:"
(lldb) p $13.get(3).big().name
(SEL) $17 = "init"
(lldb)  p $13.get(4).big().name
(SEL) $18 = "name"
(lldb)  p $13.get(5).big().name
(SEL) $19 = "setName:"
(lldb)  p $13.get(6).big().name
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享