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

339 阅读7分钟

前言

关于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-
                0x100008360LGPerson0x0000000100008360 对的上

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:对应cache_t cache

想要获取到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分析类的结构

Xnip2021-06-24_10-20-20.jpg

1.获取LGPerson的属性

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 = 4)
}
(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,V_hobby")
(lldb) p $7.get(2)
(property_t) $10 = (name = "desc", attributes = "T@\"NSString\",V_desc")
(lldb) p $7.get(3)
(property_t) $11 = (name = "idcard", attributes = "T@\"NSString\",N,V_idcard")

2.获取对象实例方法

(lldb) p/x LGPerson.class
(Class) $0 = 0x0000000100008920 LGPerson
(lldb) p (class_data_bits_t *) 0x0000000100008940 //获取class_data_bits_t 
(class_data_bits_t *) $1 = 0x0000000100008940
(lldb) p $1->data()
(class_rw_t *) $2 = 0x0000000100724a80 //获取 class_rw_t
(lldb) p $2.methods()
(const method_array_t) $3 = {
  list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
     = {
      list = {
        ptr = 0x00000001000081f8
      }
      arrayAndFlag = 4295000568
    }
  }
}
  Fix-it applied, fixed expression was: 
    $2->methods()
(lldb) p $3.list.ptr
(method_list_t *const) $4 = 0x00000001000081f8
(lldb) p * $4
(method_list_t) $5 = {
  entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 10)
}
(lldb) p $5.get(0).big()
(method_t::big) $6 = {
  name = "hobby"
  types = 0x0000000100003e78 "@16@0:8"
  imp = 0x0000000100003aa0 (KCObjcBuild`-[LGPerson hobby])
}
(lldb)  p $5.get(1).big()
(method_t::big) $7 = {
  name = "sayNB"
  types = 0x0000000100003e80 "v16@0:8"
  imp = 0x0000000100003a30 (KCObjcBuild`-[LGPerson sayNB])
}
(lldb)  p $5.get(2).big()
(method_t::big) $8 = {
  name = "setHobby:"
  types = 0x0000000100003ec4 "v24@0:8@16"
  imp = 0x0000000100003ad0 (KCObjcBuild`-[LGPerson setHobby:])
}
(lldb)  p $5.get(3).big()
(method_t::big) $9 = {
  name = "idcard"
  types = 0x0000000100003e78 "@16@0:8"
  imp = 0x0000000100003b40 (KCObjcBuild`-[LGPerson idcard])
}
(lldb)  p $5.get(4).big()
(method_t::big) $10 = {
  name = "setIdcard:"
  types = 0x0000000100003ec4 "v24@0:8@16"
  imp = 0x0000000100003b60 (KCObjcBuild`-[LGPerson setIdcard:])
}
(lldb)  p $5.get(5).big()
(method_t::big) $11 = {
  name = "init"
  types = 0x0000000100003e78 "@16@0:8"
  imp = 0x00000001000039d0 (KCObjcBuild`-[LGPerson init])
}
(lldb)  p $5.get(6).big()
(method_t::big) $12 = {
  name = "name"
  types = 0x0000000100003e78 "@16@0:8"
  imp = 0x0000000100003a40 (KCObjcBuild`-[LGPerson name])
}
(lldb)  p $5.get(7).big()
(method_t::big) $13 = {
  name = "setName:"
  types = 0x0000000100003ec4 "v24@0:8@16"
  imp = 0x0000000100003a70 (KCObjcBuild`-[LGPerson setName:])
}
(lldb)  p $5.get(8).big()
(method_t::big) $14 = {
  name = "desc"
  types = 0x0000000100003e78 "@16@0:8"
  imp = 0x0000000100003b00 (KCObjcBuild`-[LGPerson desc])
}
(lldb)  p $5.get(9).big()
(method_t::big) $15 = {
  name = "setDesc:"
  types = 0x0000000100003ec4 "v24@0:8@16"
  imp = 0x0000000100003b20 (KCObjcBuild`-[LGPerson setDesc:])
}

3.获取协议方法

Xnip2021-06-24_20-35-05.jpg

Xnip2021-06-24_10-20-20.jpg

(lldb) p/x LGPerson.class
(Class) $0 = 0x00000001000089d8 LGPerson
(lldb) p (class_data_bits_t *)0x00000001000089f8
(class_data_bits_t *) $1 = 0x00000001000089f8
(lldb) p $1->data()
(class_rw_t *) $2 = 0x000000010064eb80
(lldb) p $2.protocols()
(const protocol_array_t) $3 = {
  list_array_tt<unsigned long, protocol_list_t, RawPtr> = {
     = {
      list = {
        ptr = 0x00000001000082f0
      }
      arrayAndFlag = 4295000816
    }
  }
}
  Fix-it applied, fixed expression was: 
    $2->protocols()
(lldb) p $3.list.ptr
(protocol_list_t *const) $4 = 0x00000001000082f0
(lldb) p $4.list[0]
(protocol_ref_t) $5 = 4295002672
  Fix-it applied, fixed expression was: 
    $4->list[0]
(lldb) p (protocol_t *)$5
(protocol_t *) $6 = 0x0000000100008a30
(lldb) p * $6 
(protocol_t) $7 = {
  objc_object = {
    isa = {
      bits = 4298547400
      cls = Protocol
       = {
        nonpointer = 0
        has_assoc = 0
        has_cxx_dtor = 0
        shiftcls = 537318425
        magic = 0
        weakly_referenced = 0
        unused = 0
        has_sidetable_rc = 0
        extra_rc = 0
      }
    }
  }
  mangledName = 0x0000000100003c0a "proDelegate"
  protocols = 0x0000000100008488
  instanceMethods = 0x00000001000084a0
  classMethods = 0x0000000100008520
  optionalInstanceMethods = nil
  optionalClassMethods = nil
  instanceProperties = 0x0000000100008540
  size = 96
  flags = 0
  _extendedMethodTypes = 0x0000000100008568
  _demangledName = 0x0000000000000000
  _classProperties = nil
}
(lldb) p $7.instanceMethods //实例方法
(method_list_t *) $8 = 0x00000001000084a0
(lldb) p $8.get(0).big()
(method_t::big) $9 = {
  name = "aaaTest:"
  types = 0x0000000100003edb "v24@0:8@16"
  imp = 0x0000000000000000
}
  Fix-it applied, fixed expression was: 
    $8->get(0).big()
(lldb) p $8.get(1).big()
(method_t::big) $10 = {
  name = "name"
  types = 0x0000000100003e6a "@16@0:8"
  imp = 0x0000000000000000
}
  Fix-it applied, fixed expression was: 
    $8->get(1).big()
(lldb) p $8.get(2).big()
(method_t::big) $11 = {
  name = "setName:"
  types = 0x0000000100003edb "v24@0:8@16"
  imp = 0x0000000000000000
}
  Fix-it applied, fixed expression was: 
    $8->get(2).big()
(lldb) p $8.get(3).big()
(method_t::big) $12 = {
  name = "age"
  types = 0x0000000100003ec8 "i16@0:8"
  imp = 0x0000000000000000
}
  Fix-it applied, fixed expression was: 
    $8->get(3).big()
(lldb) p $8.get(4).big()
(method_t::big) $13 = {
  name = "setAge:"
  types = 0x0000000100003ed0 "v20@0:8i16"
  imp = 0x0000000000000000
}
  Fix-it applied, fixed expression was: 
    $8->get(4).big()
    
(lldb) p $7.classMethods //类方法
(method_list_t *) $14 = 0x0000000100008520
(lldb) p $14.get(0).big()
(method_t::big) $15 = {
  name = "aaaTest:"
  types = 0x0000000100003edb "v24@0:8@16"
  imp = 0x0000000000000000
}
  Fix-it applied, fixed expression was: 
    $14->get(0).big()
    
(lldb) p $7.instanceProperties //属性
(property_list_t *) $16 = 0x0000000100008540
(lldb) p * $16
(property_list_t) $17 = {
  entsize_list_tt<property_t, property_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 16, count = 2)
}
(lldb) p $17.get(0)
(property_t) $18 = (name = "name", attributes = "T@\"NSString\",&,N")
(lldb) p $17.get(1)
(property_t) $19 = (name = "age", attributes = "Ti,N")
(lldb) p $17.get(2)