iOS 类结构分析

前言

通过本篇文章可以了解
1.isa的走位
2.类结构的分析
3.什么是元类
4.supclass的走位
5.objc_class & objc_object

一objc_class & objc_object

@interface person : NSObject
{
    NSString *hobby;
}
@property (nonatomic, copy) NSString *cjl_name;
- (void)sayHello;
+ (void)sayBye;
@end

@implementation person
- (void)sayHello
{}
+ (void)sayBye
{}
@end


@interface student : person

@end

@implementation student

@end

我们首先定义好的类
打开之前编译好的objc4-781的源码,找到objc_class结构体定义

image.png

然后再去他的父结构体看看
image.png

  • 类的本质是一个结构体,有一个objc_class类型的isa属性
  • objc_class继承于另一个结构体objc_object,类的isa属性是从objc_object继承过来的,所以每个类都被赋予isa属性
然后我们来看看objc_class这个结构体
struct objc_class : objc_object {
    // Class ISA;     //8字节
    Class superclass; //8字节 
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
    ///省略部分代码...
}
  • isa:继承于objc_objectisa,8字节
  • superclass:父类,本质objc_class结构体指针, 8字节
  • cache: 缓存指针和函数表 8字节(不作为本章重点)
  • bits: 属性、方法等信息 8字节(一会深入探索)
通过指针偏移读取类内存中bits 中的信息

我们可以通过偏移32字节来获取到bits的信息
step.1 通过16进制打印获取类地址

(lldb) p/x objc1.class
(Class) $3 = 0x0000000100008458 person

step.2 0x0000000100008458 偏移 32字节 得到 0x0000000100008478

(lldb) p (class_data_bits_t *)0x0000000100008478
(class_data_bits_t *) $4 = 0x0000000100008478

step.3 通过提供的data()方法来获取信息 ,查看data当中都有什么

(lldb) p $4->data()
(class_rw_t *) $6 = 0x000000010064e610
(lldb) p *$6
(class_rw_t) $7 = {
  flags = 2148007936
  witness = 1
  ro_or_rw_ext = {
    std::__1::atomic = {
      Value = 4295000232
    }
  }
  firstSubclass = student
  nextSiblingClass = NSUUID
}
(lldb) 

暂时看不懂 不过有提供method()看起来应该是获取方法的列表 我们打印一下看看
step.4调用Methods()

(lldb) p $6->methods()
(const method_array_t) $10 = {
  list_array_tt = {
     = {
      list = 0x00000001000080f0
      arrayAndFlag = 4295000304
    }
  }
}

step.5 调用list属性 查看list

(lldb) p $10.list
(method_list_t *const) $11 = 0x00000001000080f0
(lldb) p *$11
(method_list_t) $12 = {
  entsize_list_tt = {
    entsizeAndFlags = 26
    count = 4
    first = {
      name = "sayHello"
      types = 0x0000000100003ef9 "v16@0:8"
      imp = 0x0000000100003b80 (KCObjc`-[person sayHello] at main.m:24)
    }
  }
}

到了这里我们就可以看到我们的sayhello实例方法存在bits里边通过get()方法验证一下
step.6查看get()

(lldb) p $12.get(0)
(method_t) $13 = {
  name = "sayHello"
  types = 0x0000000100003ef9 "v16@0:8"
  imp = 0x0000000100003b80 (KCObjc`-[person sayHello] at main.m:24)
}
(lldb) p $12.get(1)
(method_t) $14 = {
  name = "cjl_name"
  types = 0x0000000100003f0d "@16@0:8"
  imp = 0x0000000100003b90 (KCObjc`-[person cjl_name] at main.m:18)
}
(lldb) p $12.get(2)
(method_t) $15 = {
  name = "setCjl_name:"
  types = 0x0000000100003f15 "v24@0:8@16"
  imp = 0x0000000100003bc0 (KCObjc`-[person setCjl_name:] at main.m:18)
}
(lldb) p $12.get(3)
(method_t) $16 = {
  name = ".cxx_destruct"
  types = 0x0000000100003ef9 "v16@0:8"
  imp = 0x0000000100003bf0 (KCObjc`-[person .cxx_destruct] at main.m:23)
}
(lldb) p $12.get(4)
Assertion failed: (i < count), function get, file ~/runtime/objc-runtime-new.h, line 438.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.

到第5个的时候越界了
到这里我们就可以看到实例方法都在类信息当中存储
那么我们的属性储存在哪里呢
step.7 我们发现在class_rw_t类型当中发现了properties()

p $6->properties()
(const property_array_t) $17 = {
  list_array_tt = {
     = {
      list = 0x00000001000081a0
      arrayAndFlag = 4295000480
    }
  }
}
(lldb) p $17.list
(property_list_t *const) $18 = 0x00000001000081a0
(lldb) p *$18
(property_list_t) $19 = {
  entsize_list_tt = {
    entsizeAndFlags = 16
    count = 1
    first = (name = "cjl_name", attributes = "T@\"NSString\",C,N,V_cjl_name")
  }
}

step.7在通过get()方法查看一下

(lldb) p $19.get(0)
(property_t) $20 = (name = "cjl_name", attributes = "T@\"NSString\",C,N,V_cjl_name")
(lldb) p $19.get(1)
Assertion failed: (i < count), function get, file ~/runtime/objc-runtime-new.h, line 438.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.

这次是到第2个就数组越界了
属性存储在bits通过 bits -> data() -> properties() -> list获取
实例方法存储在bits 通过bits -> data() -> methods() -> list获取

类方法探索

猜测一下,对象的isa指向了类,类里有对象方法,类的isa指向元类,类方法会不会在元类里呢?
step.1 获取元类

(lldb) x/4gx objc1.class
0x100008458: 0x0000000100008430 0x000000010034c140
0x100008468: 0x0000000101831230 0x0002802400000003
(lldb) p/x 0x0000000100008430 * 0x0000000ffffffff8ULL
(unsigned long long) $1 = 0x000842f7fffbde80

step.20x000842f7fffbde80地址偏移32字节得到0x000842f7fffbdea0

(lldb) p (class_data_bits_t *)0x000842f7fffbdea0
(class_data_bits_t *) $2 = 0x000842f7fffbdea0

step.3调用data(),获取class_rw_t* data,看下data的信息

(lldb) p $5->data()
(class_rw_t *) $6 = 0x0000000101077020
(lldb) p *$6
(class_rw_t) $8 = {
  flags = 2684878849
  witness = 1
  ro_or_rw_ext = {
    std::__1::atomic = {
      Value = 4295000128
    }
  }
  firstSubclass = 0x0000000100008480
  nextSiblingClass = 0x00007fff85d59cd8
}

step.4 查看methods() 打印list 查看list内容

(lldb) p $6->methods()
(const method_array_t) $7 = {
  list_array_tt = {
     = {
      list = 0x0000000100008088
      arrayAndFlag = 4295000200
    }
  }
}
lldb) p $7.list
(method_list_t *const) $9 = 0x0000000100008088
(lldb) p *$9
(method_list_t) $10 = {
  entsize_list_tt = {
    entsizeAndFlags = 26
    count = 1
    first = {
      name = "sayBye"
      types = 0x0000000100003ef9 "v16@0:8"
      imp = 0x0000000100003b70 (KCObjc`+[person sayBye] at main.m:26)
    }
  }
}

step.5查看一下通过get()查看一下

(lldb) p $10.get(0)
(method_t) $11 = {
  name = "sayBye"
  types = 0x0000000100003ef9 "v16@0:8"
  imp = 0x0000000100003b70 (KCObjc`+[person sayBye] at main.m:26)
}
(lldb) p $10.get(1)
Assertion failed: (i < count), function get, file ~/runtime/objc-runtime-new.h, line 438.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.

总结

可以看到我们的类方法都是存储在元类当中,
通过元类bits -> methods() -> list获取类方法
通过类bits -> methods() -> list获取到对象方法

你可能感兴趣的:(iOS 类结构分析)