OC类原理

1,638 阅读15分钟

对象原理 --- 调试方式和alloc流程分析

对象原理---isa详解

一、指针

1、普通指针

首先我们先看看下面代码

    int a = 10;
    int b = 10;
    NSLog(@"%d -- %p",a,&a);
    NSLog(@"%d -- %p",b,&b);

输出的是

2019-12-21 10:18:35.828144+0800 BY_Class[58235:914046] 10 -- 0x7ffeef311164
2019-12-21 10:18:35.828362+0800 BY_Class[58235:914046] 10 -- 0x7ffeef311160

对于这些int、NSString这些基本数据类型的指针是普通指针属于值拷贝,像上面的int a = 10,是将10拷贝到a的内存地址上

2、对象指针

看看下面代码

    BYPerson *p1 = [BYPerson alloc];
    BYPerson *p2 = [BYPerson alloc];
    BYPerson *p3 = p1;
    NSLog(@"%@ -- %p",p1,&p1);
    NSLog(@"%@ -- %p",p2,&p2);
    NSLog(@"%@ -- %p",p3,&p3);

输出


2019-12-21 10:18:35.828517+0800 BY_Class[58235:914046] <BYPerson: 0x600002a7d880> -- 0x7ffeef311158
2019-12-21 10:18:35.828644+0800 BY_Class[58235:914046] <BYPerson: 0x600002a7d8e0> -- 0x7ffeef311150
2019-12-21 10:18:35.828753+0800 BY_Class[58235:914046] <BYPerson: 0x600002a7d880> -- 0x7ffeef311148
  • 会发现p1和p2的地址内里面内容都不一样,是因为每次alloc时候系统都会给对象单独分配一个内存空间,
  • p1指向这个对象时候就是把该对象的内存地址copy过来存起来,因为每个对象的内存地址都不一样,所以p1和p2里面的地址都不一样
  • 但是p1、p2、p3确都是独立的指针,所以自己本身地址是不一样的

3、数组指针

现象

代码:

    int c[4] = {1,2,3,4};
    BYNSLog(@"%p -- %p -- %p",&c,&c[0],&c[1]);

打印

BY打印: 0x7ffee6716180 -- 0x7ffee6716180 -- 0x7ffee6716184

会发现c的地址和c[0]的地址是一样的,这是因为数组的地址就是数组第一个元素的地址,并且后面元素地址比前一个大4个字节,因为数组里面元素是4个字节

验证:

我们在输出代码的后面加一个断点,然后用lldb调试下看看

image.png

我们通过c的地址打印出来会发现,c的地址里面存了四个值:1、2、3、4,因为int占4个字节,并且内存是从低位开始读取,所以打印出来的内存值是:

0x7ffee8cf4180: 0x0000000200000001 0x0000000400000003

并且由于每块内存是8个字节,但是int是4个字节,所以每个内存上面可以存储2个int类型的值

4、指针偏移

代码:

    int c[4] = {1,2,3,4};
    int *d = c;
    BYNSLog(@"%p -- %p -- %p",d,d+1,d+2);

    for (int i = 0; i < 4; i++) {

        int value = *(d + i);
        BYNSLog(@"%d",value);
    }

输出:

BY打印: 0x7ffee6d0c180 -- 0x7ffee6d0c184 -- 0x7ffee6d0c188
BY打印: 1
BY打印: 2
BY打印: 3
BY打印: 4

d是c的内存地址,因为c里面元素是int类型的,d+1相当于内存地址偏移1个元素大小,也就是4个字节,所以for循环中我们可以通过指针偏移来取到每个元素的值

上一个图更直观

image.png

二、类的结构

1、类的结构探索

代码:

        LGPerson *person = [LGPerson alloc];
        Class pclass = object_getClass(person);
        NSLog(@"%@ -- %p",person,pclass);

输出

2019-12-21 12:47:34.893387+0800 LGTest[65861:1081426] <LGPerson: 0x102034580> -- 0x100001130
(lldb) x/4gx pclass
0x100001130: 0x001d800100001109 0x0000000100afd140
0x100001140: 0x00000001020329b0 0x000000040000000f
(lldb) po 0x0000000100afd140
NSObject

(lldb) po 0x00000001020329b0
4328729008

(lldb) p 0x00000001020329b0
(long) $3 = 4328729008
(lldb) po 0x001d800100001109
8303516107936009

(lldb) p 0x001d800100001109
(long) $5 = 8303516107936009
(lldb) 

首先加个断点,然后通过x/4gx命令打印出pclass的内存,明确的是第一个0x001d800100001109是isa指针,第二个我们发现事NSObject,说明第二个是它的父类,打印其他的发现找不出来

那我们通过clang命令将文件转换成.cpp文件,查看编译后的结构 通过命令:

clang -rewrite-objc main.m -o main.cpp

我们将main.m文件转成main.cpp文件,然后打开拉到最下面

image.png

在里面我们搜索LGPerson

image.png

会发现下面有个objc_class,再搜索objc_class

image.png

Class其实就是objc_class类型,我们再到objc源码里面搜索一下objc_class

image.png

会发现objc_class继承自objc_object

所以Class说明也是一个对象,类也是一个对象,万物皆对象

objc_class里面的ISA是隐藏的,通过查找objc_object我们会发现objc_object里面有一个属性是isa,所以objc_class的isa是从objc_object继承过来的

image.png

通过点击objc_object跳转

image.png

会跳转到

image.png

会发现这个objc_class废弃了,所以所有类都源自objc_object,查看NSObject的结构

image.png

我们发现其实NSObject也就是OC对objc_object的封装,

所以我们确定类的结构其实就是一个objc_class,它集成于objc_object

image.png

为什么isa是class,因为早期isa返回的就是类,现在只是添加了nonpointter属性 我们通过+(Class)Class{}方法可以看到返回的是就是类

image.png
image.png
image.png
image.png

2、属性的存储

a、首先我们看一下类的结构体的构造

image.png

会发现里面有四个属性,

  • 第一个是isa
  • 第二个父类指针,
  • 第三个是缓存,
  • 第四个是bits

所以类的属性最可能是存在bits里面,并且我们点击class_rw_t会发现里面结构

image.png

image.png

里面有methods、properties、protocols,下面我们通过lldb将相应的内容取出来看看。

image.png

首先我们知道类的地址,并且属性是按照顺序依次排列的,只要我们知道isa、superclass、cache的内存大小,那我们就可以通过内存偏移来得到bits的内存地址,然后取出bits里面的内容:

  • 1、ISA是指针,是8个字节,
  • 2、superclass也是指针、8个字节
  • 3、cache_t结构形式为:

image.png

image.png
其中_buckets是结构体:8字节,mask_t是uint32_t:4字节,所以我们得到cache_t是16个字节

image.png

b、lldb调试得到class的属性

那我们就就得到类的内存地址偏移32个字节就得到bits的内存地址,下面我们就用lldb操作一下:

1、我们通过lldb调出class里面的内容

2019-12-21 14:42:53.811392+0800 LGTest[71379:1223891] <LGPerson: 0x101d4a400> -- 0x100001200
(lldb) x/4gx pclass
0x100001200: 0x001d8001000011d9 0x0000000100afd140
0x100001210: 0x0000000101d48940 0x000000040000000f
(lldb) po 0x100001208
<NSObject: 0x100001208>
(lldb) po 0x100001220
objc[71379]: Attempt to use unknown class 0x101d48740.
4294971936
(lldb) p 0x100001220
(long) $3 = 4294971936
(lldb) p (class_data_bits_t *)0x100001220
(class_data_bits_t *) $4 = 0x0000000100001220
(lldb) po $4->data()
0x0000000101d48740
(lldb) p $4->data()
(class_rw_t *) $6 = 0x0000000101d48740
(lldb) p *$7
error: use of undeclared identifier '$7'
(lldb) p *$6
(class_rw_t) $7 = {
  flags = 2148139008
  version = 0
  ro = 0x0000000100001178
  methods = {
    list_array_tt<method_t, method_list_t> = {
       = {
        list = 0x00000001000010c8
        arrayAndFlag = 4294971592
      }
    }
  }
  properties = {
    list_array_tt<property_t, property_list_t> = {
       = {
        list = 0x0000000100001160
        arrayAndFlag = 4294971744
      }
    }
  }
  protocols = {
    list_array_tt<unsigned long, protocol_list_t> = {
       = {
        list = 0x0000000000000000
        arrayAndFlag = 0
      }
    }
  }
  firstSubclass = nil
  nextSiblingClass = NSDate
  demangledName = 0x0000000100000f46 "LGPerson"
}
(lldb) po $7.properties

(lldb) p $7.properties
(property_array_t) $9 = {
  list_array_tt<property_t, property_list_t> = {
     = {
      list = 0x0000000100001160
      arrayAndFlag = 4294971744
    }
  }
}
(lldb) p $9[0]
error: type 'property_array_t' does not provide a subscript operator
(lldb) p *$9
error: indirection requires pointer operand ('property_array_t' invalid)
(lldb) p $9.list
(property_list_t *) $10 = 0x0000000100001160
(lldb) p $10.first
(property_t) $11 = (name = "address1", attributes = "T@\"NSString\",C,N,V_address1")
  Fix-it applied, fixed expression was: 
    $10->first
(lldb) p ¥7.ro
error: use of undeclared identifier '¥7'
(lldb) p $7.ro
(const class_ro_t *) $12 = 0x0000000100001178
(lldb) p *$12
(const class_ro_t) $13 = {
  flags = 388
  instanceStart = 8
  instanceSize = 24
  reserved = 0
  ivarLayout = 0x0000000100000f4f "\x02"
  name = 0x0000000100000f46 "LGPerson"
  baseMethodList = 0x00000001000010c8
  baseProtocols = 0x0000000000000000
  ivars = 0x0000000100001118
  weakIvarLayout = 0x0000000000000000
  baseProperties = 0x0000000100001160
}
(lldb) p $13.baseProperties
(property_list_t *const) $14 = 0x0000000100001160
(lldb) p $13.weakIvarLayout
(const uint8_t *const) $15 = 0x0000000000000000
(lldb) p *$14
(property_list_t) $16 = {
  entsize_list_tt<property_t, property_list_t, 0> = {
    entsizeAndFlags = 16
    count = 1
    first = (name = "address1", attributes = "T@\"NSString\",C,N,V_address1")
  }
}
(lldb) p *$15
error: Couldn't apply expression side effects : Couldn't dematerialize a result variable: couldn't read its memory
(lldb) p $13.flags
(uint32_t) $18 = 388
(lldb) p $13.ivarLayout
(const uint8_t *const) $19 = 0x0000000100000f4f "\x02"
(lldb) p *$19
(uint8_t) $20 = '\x02'
(lldb) p 0x0000000100000f4f
(long) $21 = 4294971215
(lldb) p $13.ivars
(const ivar_list_t *const) $22 = 0x0000000100001118
(lldb) p *$22
(const ivar_list_t) $23 = {
  entsize_list_tt<ivar_t, ivar_list_t, 0> = {
    entsizeAndFlags = 32
    count = 2
    first = {
      offset = 0x00000001000011c8
      name = 0x0000000100000f75 "by_hobby"
      type = 0x0000000100000fa3 "@\"NSString\""
      alignment_raw = 3
      size = 8
    }
  }
}
(lldb) p $23.get(1)
(ivar_t) $24 = {
  offset = 0x00000001000011d0
  name = 0x0000000100000f7e "_address1"
  type = 0x0000000100000fa3 "@\"NSString\""
  alignment_raw = 3
  size = 8
}
(lldb) p $13.baseMethodList
(method_list_t *const) $25 = 0x00000001000010c8
(lldb) p *$25
(method_list_t) $26 = {
  entsize_list_tt<method_t, method_list_t, 3> = {
    entsizeAndFlags = 26
    count = 3
    first = {
      name = "address1"
      types = 0x0000000100000f90 "@16@0:8"
      imp = 0x0000000100000dd0 (LGTest`-[LGPerson address1] at LGPerson.h:17)
    }
  }
}
(lldb) p $26.get(1)
(method_t) $27 = {
  name = "setAddress1:"
  types = 0x0000000100000f98 "v24@0:8@16"
  imp = 0x0000000100000e00 (LGTest`-[LGPerson setAddress1:] at LGPerson.h:17)
}
(lldb) p $26.get(2)
(method_t) $28 = {
  name = ".cxx_destruct"
  types = 0x0000000100000f88 "v16@0:8"
  imp = 0x0000000100000e40 (LGTest`-[LGPerson .cxx_destruct] at LGPerson.m:11)
}
(lldb) p $26.get(3)
Assertion failed: (i < count), function get, file /Users/zhaojing/Downloads/Logic iOS 大师班/20191220-大师班-第4节课-OC类原理/01--课堂代码/001-类&元类的创建时机/runtime/objc-runtime-new.h, line 117.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.
(lldb) 

###总结:

通过上面调试我们可以得到:

  • 1、属性是存储在类的bits的class_rw_t里面的class_ro_t的baseProperties里面
  • 2、成员变量是在class_ro_t的ivars里面,
  • 3、实例方法是在class_ro_t 的baseMethodList里面

3、方法的存储

首先我们创建一个类,写一个实例方法和一个类方法并实现以下,然后创建完对象后打好断点

image.png

image.png

###a、实例方法的存储

####lldb调试流程

(lldb) p/x pclass      //打印出类的内存
(Class) $0 = 0x0000000100001238 LGPerson
(lldb) p (class_data_bits_t *)0x0000000100001258   //根据上面分析bits前面的属性内存占了32个字节,且类的内存地址是首元素的内存地址,所以我们可以通过类的内存地址偏移32个字节可以得到bits的内存地址并进行强转一下
(class_data_bits_t *) $1 = 0x0000000100001258
(lldb) p *$1
(class_data_bits_t) $2 = (bits = 4313984164)
(lldb) p $2->data()     //取出bits里面的class_rw_t
(class_rw_t *) $3 = 0x0000000101222ca0
  Fix-it applied, fixed expression was: 
    $2.data()
(lldb) p $3.ro     //取出class_rw_t 里面的class_ro_t
(const class_ro_t *) $4 = 0x00000001000011b0
  Fix-it applied, fixed expression was: 
    $3->ro
(lldb) p *$4   //取出class_ro_t的内容
(const class_ro_t) $5 = {
  flags = 388
  instanceStart = 8
  instanceSize = 24
  reserved = 0
  ivarLayout = 0x0000000100000f4f "\x02"
  name = 0x0000000100000f46 "LGPerson"
  baseMethodList = 0x00000001000010e8
  baseProtocols = 0x0000000000000000
  ivars = 0x0000000100001150
  weakIvarLayout = 0x0000000000000000
  baseProperties = 0x0000000100001198
}
(lldb) p $5.baseMethodList  //拿到class_ro_t里面的baseMethodList
(method_list_t *const) $6 = 0x00000001000010e8
(lldb) p *$6
(method_list_t) $7 = {
  entsize_list_tt<method_t, method_list_t, 3> = {
    entsizeAndFlags = 26
    count = 4
    first = {
      name = "address1"
      types = 0x0000000100000f98 "@16@0:8"
      imp = 0x0000000100000dd0 (LGTest`-[LGPerson address1] at LGPerson.h:17)
    }
  }
}
(lldb) p $7.get(1)     //通过下标取出baseMethodList里面的元素打印出来
(method_t) $8 = {
  name = "setAddress1:"
  types = 0x0000000100000fa0 "v24@0:8@16"
  imp = 0x0000000100000e00 (LGTest`-[LGPerson setAddress1:] at LGPerson.h:17)
}
(lldb) p $7.get(2)
(method_t) $9 = {
  name = ".cxx_destruct"
  types = 0x0000000100000f90 "v16@0:8"
  imp = 0x0000000100000e40 (LGTest`-[LGPerson .cxx_destruct] at LGPerson.m:11)
}
(lldb) p $7.get(3)
(method_t) $10 = {
  name = "run"
  types = 0x0000000100000f90 "v16@0:8"
  imp = 0x0000000100000db0 (LGTest`-[LGPerson run] at LGPerson.m:12)
}
(lldb) p $7.get(4)   //方法列表里面就四个方法
Assertion failed: (i < count), function get, file /Users/zhaojing/Downloads/Logic iOS 大师班/20191220-大师班-第4节课-OC类原理/01--课堂代码/001-类&元类的创建时机/runtime/objc-runtime-new.h, line 117.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.
(lldb) 

####总结:

类的实例方法是存储在类的bits的class_rw_t的class_ro_t的baseMethodList里面,并且只有实例方法没有类方法

b、类方法的存储

####ldb调试流程

(lldb) x/4gx pclass   //取到类的内存地址
0x1000023a0: 0x001d800100002379 0x0000000100afe140
0x1000023b0: 0x0000000101919d30 0x0000000200000003
(lldb) p/x 0x001d800100002379 & 0x00007ffffffffff8   //拿到元类的内存地址
(long) $1 = 0x0000000100002378
(lldb) x/4gx $1  //拿到元类的内存结构
0x100002378: 0x001d800100afe0f1 0x0000000100afe0f0
0x100002388: 0x0000000101919e10 0x0000000500000007
(lldb) po 0x0000000100afe0f0  //拿到元类的父类,确定拿到的元类是正确的
NSObject

(lldb) p (class_data_bits_t *)0x100002398 //拿到元类的bits内存地址,并强转成class_data_bits_t
(class_data_bits_t *) $3 = 0x0000000100002398
(lldb) p *$3  //取出元类的bits内存地址的内容
(class_data_bits_t) $4 = (bits = 4321287200)
(lldb) p $4->data()  //拿到bits里面的class_rw_t
(class_rw_t *) $5 = 0x0000000101919c20
  Fix-it applied, fixed expression was: 
    $4.data()
(lldb) p $5.ro //拿到class_rw_t里的class_ro_t
(const class_ro_t *) $6 = 0x00000001000021d0
  Fix-it applied, fixed expression was: 
    $5->ro
(lldb) p *$6   //取出class_ro_t里面的值
(const class_ro_t) $7 = {
  flags = 389
  instanceStart = 40
  instanceSize = 40
  reserved = 0
  ivarLayout = 0x0000000000000000
  name = 0x0000000100001f84 "LGPerson"
  baseMethodList = 0x00000001000021b0
  baseProtocols = 0x0000000000000000
  ivars = 0x0000000000000000
  weakIvarLayout = 0x0000000000000000
  baseProperties = 0x0000000000000000
}
(lldb) p $7.baseMethodList //拿到class_ro_t里面的方法列表
(method_list_t *const) $8 = 0x00000001000021b0
(lldb) p *$8
(method_list_t) $9 = {
  entsize_list_tt<method_t, method_list_t, 3> = {
    entsizeAndFlags = 26
    count = 1
    first = {
      name = "ear"
      types = 0x0000000100001f8f "v16@0:8"
      imp = 0x0000000100001be0 (LGTest`+[LGPerson ear] at LGPerson.m:16)
    }
  }
}
(lldb) 

####总结:

然后我们确定发现类的类方法是存在元类里面

4、通过系统提供的api来验证属性、实例方法、类方法的存储位置

写一个函数:

void testObjc_Ivars_and_properise(Class class){

    unsigned int count = 0;
    Ivar *ivars = class_copyIvarList(class, &count);
    for (unsigned int i = 0; i < count; i++) {
        Ivar const ivar = ivars[i];//如何知道Ivar是一个数组,且里面元素是ivar const类型
        //获取成员变量名
        const char *cName = ivar_getName(ivar);
        NSString *ivarName = [NSString stringWithUTF8String:cName];
        NSLog(@"class_ivar:%@",ivarName);
    }

    unsigned const pCount = 0;
    objc_property_t *properties = class_copyPropertyList(class, pCount);
    for (unsigned int i = 0; i < pCount; i++) {
        objc_property_t const property = properties[i];
        //获取属性名
        NSString *properityName = [NSString stringWithUTF8String:property_getName(property)];
        NSLog(@"class_properity:%@",properityName);
    }
}

//我们把LGPerson传进去

        LGPerson *person = [LGPerson alloc];
        Class pclass = object_getClass(person);
        NSLog(@"%@ -- %p",person,pclass);
        testObjc_Ivars_and_properise(pclass);

打印:

BY打印: class_ivar:by_hobby
BY打印: class_ivar:_address1
BY打印: class_properity:address1

方法:

再写一个函数

void testInstanceMethod(Class class){
    unsigned int count = 0;
    Method *methods = class_copyMethodList(class, &count);
    for (unsigned int i = 0; i < count; i++) {
        Method const method = methods[i];
        //获取方法名
        NSString *methodName = NSStringFromSelector(method_getName(method));
        NSString *methodName1 = [NSString stringWithUTF8String:method_getTypeEncoding(method)];
        NSLog(@"class_method:%@  --- %@",methodName,methodName1);
    }
    free(methods);
}

首先我们把LGPerson类传进去,然后将LGPerson的元类传进去,调用两次,调用代码:

        LGPerson *person = [LGPerson alloc];
        Class pclass = object_getClass(person);
        testInstanceMethod(pclass);
        BYNSLog(@"下面是元类方法");
        const char *className = class_getName(pclass);
        Class metaClass = objc_getMetaClass(className);
        testInstanceMethod(metaClass);

调用结果:

BY打印: class_method:address1  --- @16@0:8
BY打印: class_method:setAddress1:  --- v24@0:8@16
BY打印: class_method:.cxx_destruct  --- v16@0:8
BY打印: class_method:run  --- v16@0:8
BY打印: 下面是元类方法
BY打印: class_method:ear  --- v16@0:8

我们会发现实例方法是在类对象中,但是类方法却在元类中,

另外,objc框架还提供了函数用来从类中查找方法的指针功能,如果能查到就说明该方法在类中,假如查不到就会返回0x0,这个功能可以用来验证该方法是否存在该类中。

函数:

//验证方法在类中是否存在

void testMethod_class(Class class){

    const char *className = class_getName(class);
    Class metaClass = objc_getMetaClass(className);
    //得到类的实例方法,可以从类中找到实例方法,从元类中找到类方法,并且只声明,没实现的话,是找不到相应的方法的,
    Method method1 = class_getInstanceMethod(class, @selector(run));
    Method method2 = class_getInstanceMethod(metaClass, @selector(run));
    Method method3 = class_getInstanceMethod(class, @selector(ear));
    Method method4 = class_getInstanceMethod(metaClass, @selector(ear));
    BYNSLog(@"instanceMethod:%p -- %p -- %p -- %p",method1,method2,method3,method4);
    //得到类的类方法,可以从类中找到类方法,也可以从元类在找到类方法,但是找不到实例方法,没实现的话,是找不到相应的方法的,
    Method method5 = class_getClassMethod(class, @selector(run));
    Method method6 = class_getClassMethod(metaClass, @selector(run));
    Method method7 = class_getClassMethod(class, @selector(ear));
    Method method8 = class_getClassMethod(metaClass, @selector(ear));
    BYNSLog(@"classMetod:%p -- %p -- %p -- %p",method5,method6,method7,method8);
    //获取类中的方法实现,都可以获取到指针,,不知道啥玩意,c随便写都能获取到imp
    
    IMP imp1 = class_getMethodImplementation(class, @selector(run));
    IMP imp2 = class_getMethodImplementation(metaClass, @selector(run));
    IMP imp3 = class_getMethodImplementation(class, @selector(ear));
    IMP imp4 = class_getMethodImplementation(metaClass, @selector(ear));
    IMP imp5 = class_getMethodImplementation(metaClass, @selector(ear1));

    BYNSLog(@"methodIMP:%p -- %p -- %p -- %p -- %p",imp1,imp2,imp3,imp4,imp5);

}

调用结果:

BY打印: instanceMethod:0x100002268 -- 0x0 -- 0x0 -- 0x1000021b8
BY打印: classMetod:0x0 -- 0x0 -- 0x1000021b8 -- 0x1000021b8
BY打印: methodIMP:0x100001bd0 -- 0x1003449c0 -- 0x1003449c0 -- 0x100001be0 -- 0x1003449c0

我们根据得到的结果分析一下

class_getInstanceMethod
class_getClassMethod
class_getMethodImplementation

这三个方法的实现: ####(1)class_getInstanceMethod实现 #####结果: 得到类的实例方法,可以从类中找到实例方法,从元类中找到类方法,并且只声明,没实现的话,是找不到相应的方法的 #####分析: 拿到源码:

image.png
image.png
image.png
image.png
下面的看不懂了,待解决
image.png

####(2)class_getClassMethod实现 #####结果: 得到类的类方法,可以从类中找到类方法,也可以从元类在找到类方法,但是找不到实例方法,没实现的话,是找不到相应的方法的 #####分析:

image.png
image.png
#####结论: 我们发现getMeta传进去的如果是元类的话就返回自己,所以class_getClassMethod中其实是让类的元类去调用class_getInstanceMethod,根据结果我们总结出,对于元类来说,类方法也是他们的实例方法

####(3)class_getMethodImplementation #####结果: 获取类中的方法实现指针,都可以获取到指针,并且从元类中获取实例方法、从类中获取类方法、从元类中获取不存在的方法返回的指针都是一样的 #####分析:

image.png
image.png
#####结论: 综合上面结果我们会发现如果找不到IMP,系统会默认返回_objc_msgForward

#总结:

image.png
isa流向图:
image.png
继承图:
image.png
isa结构图(不觉明历):
image.png
image.png
类的结构图:
image.png

bits的结构图:

image.png

#问题: 1、NSObject是对objc_object的封装,那NSObject(class)和objc_class是什么关系? 2、类的协议是存在哪的? 3、class_getInstanceMethod里面的具体实现? 4、为啥class_data_bits_t里面的method_array_t、property_array_t里面存的东西都很乱?