OC底层原理08 - 类结构探索(3)

在类结构探索(1)中,对类结构中的isa进行了探索,在类结构探索(2)中,对cache_t进行了探索,接下来将对类结构中的bits属性进行探索。

变量的种类

在探索之前,先了解一下,在OC中,到底有几种形式的变量。

在类里面,有三种形式的变量:

  • 成员变量
    在OC类中,定义在{}中的变量。
@interface LGPerson ()
{
    NSString* englishName;
    int height;
}
@end
  • 实例变量
    通过当前对象类型具备实例化变量,是一种特殊的成员变量。实例变量主要是判断是不是对象。
@interface LGPerson ()
{
    LGStudent* stu;
}
@end
  • 属性
    在OC中是通过@property开头定义,相当于带下划线成员变量 + setter + getter方法变量
@interface LGPerson : NSObject
@property(nonatomic, strong)NSString* name;
@property(nonatomic, strong)NSString* nickName;
@end

探索

之前分析了类结构,isa指针存储类信息cache存储了调用的对象方法。剩下class_data_bits_t bits这个还没进行探索,那会不会在这个结构里面存储了变量信息?接下来就对class_data_bits_t bits进行探索。

在之前的分析中知道存在对象的类元类,接下来,分别对对象的类元类进行探索。

探索类的bits属性

  • 获取class_data_bits_t bits的首地址
    在类中,isa8字节superclass8字节cache16字节,因此要求得bits首地址,需要将类首地址偏移32字节
//找到类的首地址
(lldb) p/x pClass
(Class) $0 = 0x00000001000083a8 LGPerson

//获取bits首地址
(lldb) p/x 0x00000001000083a8+0x20
(long) $1 = 0x00000001000083c8

//将bits首地址强转成 class_data_bits_t* 类型
p/x (class_data_bits_t*)0x00000001000083c8
(class_data_bits_t *) $2 = 0x00000001000083c8

//读取 class_data_bits_t 结构体内容
(lldb) p *$2
(class_data_bits_t) $3 = (bits = 4302045844)
  • 探索class_rw_t
    bits = 4302045844无法理解更深的含义,但bits提供了data方法用于获取更多的信息。bits.data()返回的是class_rw_t结构体,class_rw_t为获取类的可读写信息
//获取class_rw_t结构体的地址
(lldb) p $3.data()
(class_rw_t *) $4 = 0x00000001006c0290

//获取class_rw_t结构体内容
(lldb) p *$4
(class_rw_t) $5 = {
  flags = 2148007936
  witness = 0
  ro_or_rw_ext = {
    std::__1::atomic = {
      Value = 4295000200
    }
  }
  firstSubclass = nil
  nextSiblingClass = NSUUID
}

class_rw_t结构体如下:

struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint32_t version;

    const class_ro_t *ro;

    method_array_t methods;        //方法列表
    property_array_t properties;    //属性列表
    protocol_array_t protocols;      //协议列表

    Class firstSubclass;
    Class nextSiblingClass;

    char *demangledName;
}

我们知道,OC类的方法、属性、协议都是可以动态添加,也就是可读可写的,从上面的源码中,可以发现确实是有对应的成员来保存方法、属性、协议的信息。而从该结构体的名字class_rw_t,也暗含了上述的方法、属性、协议信息,是可读可写的。另外,我们知道Class类里面的成员变量是不可以动态添加的,也就是属于只读内容,相应的,可以推断const class_ro_t *ro;就是指向了该部分内容信息的指针。同样,查看其源码

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;//instance对象占用的内存空间
#ifdef __LP64__
    uint32_t reserved;
#endif

    const uint8_t * ivarLayout;
    
    const char * name;//类名
    method_list_t * baseMethodList;//方法列表
    protocol_list_t * baseProtocols;//协议列表
    const ivar_list_t * ivars;//成员变量列表

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;//属性列表

    method_list_t *baseMethods() const {
        return baseMethodList;
    }
};

这个结构体里面,就存放了一些类相关的只读信息

  • uint32_t instanceSize;——instance对象占用的内存空间
  • const char * name;——类名
  • const ivar_list_t * ivars;——类的成员变量列表

以上对class_rw_t结构体和class_ro_t结构体的源码进行了分析,接下来通过lldb来查看一下,class_rw_t结构体和class_ro_t结构体中存储的内容是不是如我们所分析的一样。

探索类的bits属性中的methods

  • 获取方法列表methods
//读取class_rw_t结构体中的methods
(lldb) p $5.methods()
(const method_array_t) $6 = {
  list_array_tt = {
     = {
      list = 0x00000001000080d0
      arrayAndFlag = 4295000272
    }
  }
}

//获取list的地址
(lldb) p $6.list
(method_list_t *const) $7 = 0x00000001000080d0

//输出method_list_t
(lldb) p *$7
(method_list_t) $8 = {
  entsize_list_tt = {
    entsizeAndFlags = 26
    count = 9
    first = {
      name = "setEnglishName:"
      types = 0x0000000100003f7a "v24@0:8@16"
      imp = 0x0000000100003c00 (KCObjc`-[LGPerson setEnglishName:])
    }
  }
}

(lldb) p $8.get(0)
(method_t) $9 = {
  name = "sayInstanceMethod"
  types = 0x0000000100003f73 "v16@0:8"
  imp = 0x0000000100003cd0 (KCObjc`-[LGPerson sayInstanceMethod])
}
(lldb) p $8.get(1)
(method_t) $10 = {
  name = ".cxx_destruct"
  types = 0x0000000100003f73 "v16@0:8"
  imp = 0x0000000100003da0 (KCObjc`-[LGPerson .cxx_destruct])
}
(lldb) p $8.get(2)
(method_t) $11 = {
  name = "name"
  types = 0x0000000100003f87 "@16@0:8"
  imp = 0x0000000100003d00 (KCObjc`-[LGPerson name])
}
(lldb) p $8.get(3)
(method_t) $12 = {
  name = "setName:"
  types = 0x0000000100003f8f "v24@0:8@16"
  imp = 0x0000000100003d20 (KCObjc`-[LGPerson setName:])
}
(lldb) p $8.get(4)
(method_t) $13 = {
  name = "setNickName:"
  types = 0x0000000100003f8f "v24@0:8@16"
  imp = 0x0000000100003d70 (KCObjc`-[LGPerson setNickName:])
}
(lldb) p $8.get(5)
(method_t) $14 = {
  name = "nickName"
  types = 0x0000000100003f87 "@16@0:8"
  imp = 0x0000000100003d50 (KCObjc`-[LGPerson nickName])
}
(lldb) p $8.get(6)
Assertion failed: (i < count), function get, file /Users/macbookpro/Desktop/study/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)
Assertion failed: (i < count), function get, file /Users/macbookpro/Desktop/study/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 属性中,通过 bits --> methods() --> list获取实例方法列表,类中的方法列表除了包括实例方法,还包括属性的set 方法 和 get方法

探索类的bits属性中的properties

  • 获取方法列表properties
//读取class_rw_t结构体中的properties
(lldb) p $5.properties()
(const property_array_t) $15 = {
  list_array_tt = {
     = {
      list = 0x00000001000081c8
      arrayAndFlag = 4295000520
    }
  }
}

//获取list的地址
(lldb) p $15.list
(property_list_t *const) $16 = 0x00000001000081c8

////输出property_list_t
(lldb) p *$16
(property_list_t) $17 = {
  entsize_list_tt = {
    entsizeAndFlags = 16
    count = 2
    first = (name = "name", attributes = "T@\"NSString\",&,N,V_name")
  }
}
(lldb) p $17.get(0)
(property_t) $18 = (name = "name", attributes = "T@\"NSString\",&,N,V_name")
(lldb) p $17.get(1)
(property_t) $19 = (name = "nickName", attributes = "T@\"NSString\",&,N,V_nickName")
(lldb) p $17.get(2)
Assertion failed: (i < count), function get, file /Users/macbookpro/Desktop/study/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属性中,通过bits --> properties() --> list获取属性列表

探索类的bits属性中的ro

  • 通过ro方法获取class_ro_t结构体
//读取class_rw_t结构体中的ro地址
(lldb) p $5.ro()
(const class_ro_t *) $6 = 0x00000001000080a0

//读取class_rw_t结构体中的ro内容
(lldb) p *$6
(const class_ro_t) $7 = {
  flags = 388
  instanceStart = 8
  instanceSize = 40
  reserved = 0
  ivarLayout = 0x0000000100003f6e "\x01\x12"
  name = 0x0000000100003f65 "LGPerson"
  baseMethodList = 0x00000001000080e8
  baseProtocols = 0x0000000000000000
  ivars = 0x0000000100008180
  weakIvarLayout = 0x0000000000000000
  baseProperties = 0x0000000100008208
  _swiftMetadataInitializer_NEVER_USE = {}
}

//获取ro中的ivars中的地址
(lldb) p $7.ivars
(const ivar_list_t *const) $8 = 0x0000000100008180

//读取ivars中的内容
(lldb) p *$8
(const ivar_list_t) $9 = {
  entsize_list_tt = {
    entsizeAndFlags = 32
    count = 4
    first = {
      offset = 0x00000001000082e8
      name = 0x0000000100003e85 "englishName"
      type = 0x0000000100003f83 "@\"NSString\""
      alignment_raw = 3
      size = 8
    }
  }
}
(lldb) p $9.get(0)
(ivar_t) $10 = {
  offset = 0x00000001000082e8
  name = 0x0000000100003e85 "englishName"
  type = 0x0000000100003f83 "@\"NSString\""
  alignment_raw = 3
  size = 8
}
(lldb) p $9.get(1)
(ivar_t) $11 = {
  offset = 0x00000001000082f0
  name = 0x0000000100003e91 "height"
  type = 0x0000000100003f8f "i"
  alignment_raw = 2
  size = 4
}
(lldb) p $9.get(2)
(ivar_t) $12 = {
  offset = 0x00000001000082f8
  name = 0x0000000100003e98 "_name"
  type = 0x0000000100003f83 "@\"NSString\""
  alignment_raw = 3
  size = 8
}
(lldb) p $9.get(3)
(ivar_t) $13 = {
  offset = 0x0000000100008300
  name = 0x0000000100003e9e "_nickName"
  type = 0x0000000100003f83 "@\"NSString\""
  alignment_raw = 3
  size = 8
}
(lldb) p $9.get(4)
Assertion failed: (i < count), function get, file /Users/macbookpro/Desktop/study/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属性中,通过bits --> ro() --> list获取成员变量列表
注意:此处有_name、_nickName两个成员变量。这是哪里来的呢?这是因为在定义属性(即@property)的时候系统会定义一个对个属性名加上下划线变量

总结

  • 对象的实例方法对象的属性对象的成员变量都存在类的bits属性中。
  • 通过bits --> methods() --> list获取实例方法列表
  • 通过bits --> properties() --> list获取属性列表
  • 通过bits --> ro() --> list获取成员变量列表

探索元类的bits属性

  • 获取元类中class_data_bits_t bits的首地址
//读取类对象的内容
(lldb) x/4gx LGPerson.class
0x1000082e0: 0x00000001000082b8 0x000000010034c140
0x1000082f0: 0x0000000100346460 0x0000802400000000

//通过isa找到元类首地址
(lldb) p/x 0x00000001000082b8 & 0x00007ffffffffff8ULL
(unsigned long long) $36 = 0x00000001000082b8

//将元类的地址偏移32字节,找到class_data_bits_t地址
(lldb) p/x 0x00000001000082b8 + 0x20
(long) $37 = 0x00000001000082d8

//强转成class_data_bits_t地址
(lldb) p/x (class_data_bits_t*)$37
(class_data_bits_t *) $38 = 0x00000001000082d8

//输出class_data_bits_t结构体中的内容
(lldb) p *$38
(class_data_bits_t) $39 = (bits = 4322599572)

此时有个疑问,那类方法存在哪里呢?
我们知道,类对象是以元类为模板的对象,那类方法会不会是存在元类的bits属性中呢?带着这个疑问继续探索。

探索元类的bits属性中的methods

//获取class_data_bits_t中可读可写的信息
(lldb) p $39.data()
(class_rw_t *) $40 = 0x0000000101a5a290


(lldb) p *$40
(class_rw_t) $41 = {
  flags = 2684878849
  witness = 1
  ro_or_rw_ext = {
    std::__1::atomic = {
      Value = 4295000120
    }
  }
  firstSubclass = nil
  nextSiblingClass = 0x00007fff8bd2ecf8
}

//读取class_rw_t结构体中的methods
(lldb) p $41.methods()
(const method_array_t) $42 = {
  list_array_tt = {
     = {
      list = 0x0000000100008080
      arrayAndFlag = 4295000192
    }
  }
}

//获取list的地址
(lldb) p $42.list
(method_list_t *const) $43 = 0x0000000100008080

//输出method_list_t
(lldb) p *$43
(method_list_t) $44 = {
  entsize_list_tt = {
    entsizeAndFlags = 26
    count = 1
    first = {
      name = "sayClassMethod"
      types = 0x0000000100003f73 "v16@0:8"
      imp = 0x0000000100003ca0 (KCObjc`+[LGPerson sayClassMethod])
    }
  }
}
(lldb) p $44.get(0)
(method_t) $45 = {
  name = "sayClassMethod"
  types = 0x0000000100003f73 "v16@0:8"
  imp = 0x0000000100003ca0 (KCObjc`+[LGPerson sayClassMethod])
}
(lldb) p $44.get(1)
Assertion failed: (i < count), function get, file /Users/macbookpro/Desktop/study/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属性中,也是通过bits --> methods() --> list获取类方法列表

总结:

  • 类方法存在元类的bits属性中。
  • 通过bits --> methods() --> list获取类方法列表。

探索元类的bits属性中的properties

  • 获取元类中的properties
(lldb) p $6.properties()
(const property_array_t) $7 = {
  list_array_tt = {
     = {
      list = 0x0000000000000000
      arrayAndFlag = 0
    }
  }
}
(lldb) p $7.list
(property_list_t *const) $8 = 0x0000000000000000
(lldb) p *$8
error: Couldn't apply expression side effects : Couldn't dematerialize a result variable: couldn't read its memory

总结

元类的bits属性中并未存储与对象的属性相关的内容。

至此,我们完成bits属性的探索也完成了。

你可能感兴趣的:(OC底层原理08 - 类结构探索(3))