struct cache_t {
private:
explicit_atomic
union {
struct {
explicit_atomic
#if __LP64__
uint16_t _flags; //2
#endif
uint16_t _occupied;//2
};
explicit_atomic
};
union isa_t {
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
uintptr_t bits;
private:
// Accessing the class requires custom ptrauth operations, so
// force clients to go through setClass/getClass by making this
// private.
Class cls;
struct objc_object {
private:
isa_t isa;
struct objc_class : objc_object {
objc_class(const objc_class&) = delete;
objc_class(objc_class&&) = delete;
void operator=(const objc_class&) = delete;
void operator=(objc_class&&) = delete;
// Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
class_rw_t *data() const {
return bits.data();
}
#define FAST_DATA_MASK 0x00007ffffffffff8UL
class_rw_t* data() const {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint16_t witness;
#if SUPPORT_INDEXED_ISA
uint16_t index;
#endif
explicit_atomic
Class firstSubclass;
Class nextSiblingClass;
(lldb) x/4gx LGPerson.class
0x100008278: 0x00000001000082a0 0x0000000100801140
0x100008288: 0x0000000100ad4880 0x0002802c00000003
(lldb) p/x 0x100008278+0x20
(long) $1 = 0x0000000100008298
(lldb) p (class_data_bits_t *)0x0000000100008298
(class_data_bits_t *) $2 = 0x0000000100008298
(lldb) p $2->data()
(class_rw_t *) $3 = 0x0000000100ad4610
(lldb) p *$3
(class_rw_t) $4 = {
flags = 2148007936
witness = 1
ro_or_rw_ext = {
std::__1::atomic
Value = 4295000136
}
}
firstSubclass = nil
nextSiblingClass = 0x00007ff8428edac8
}
(lldb) p $3.properties()
(const property_array_t) $5 = {
list_array_tt
= {
list = {
ptr = 0x00000001000081a8
}
arrayAndFlag = 4295000488
}
}
}
Fix-it applied, fixed expression was:
$3->properties()
(lldb) p $5.list
(const RawPtr
ptr = 0x00000001000081a8
}
(lldb) p $6.ptr
(property_list_t *const) $7 = 0x00000001000081a8
(lldb) p *$7
(property_list_t) $8 = {
entsize_list_tt
}
(lldb) p $8.get(0)
(property_t) $9 = (name = "name", attributes = "T@\"NSString\",C,N,V_name")
(lldb)
(lldb) p $8.get(1)
(property_t) $12 = (name = "hobby", attributes = "T@\"NSString\",C,N,V_hobby")
(lldb)
(lldb) p $3->methods()
(const method_array_t) $10 = {
list_array_tt
= {
list = {
ptr = 0x0000000100008090
}
arrayAndFlag = 4295000208
}
}
}
(lldb) p $10.list.ptr
(method_list_t *const) $11 = 0x0000000100008090
(lldb) p $10.list
(const method_list_t_authed_ptr
ptr = 0x0000000100008090
}
(lldb) p $13.ptr
(method_list_t *const) $14 = 0x0000000100008090
(lldb) p *$14
(method_list_t) $15 = {
entsize_list_tt
}
(lldb) p $15.get(0)
(method_t) $16 = {}
(lldb) p $15.get(0).big()
(method_t::big) $17 = {
name = "satNB"
types = 0x0000000100003f92 "v16@0:8"
imp = 0x0000000100003ca0 (KCObjcBuild`-[LGPerson satNB])
}
(lldb) p $15.get(1).big()
(method_t::big) $18 = {
name = "hobby"
types = 0x0000000100003f8a "@16@0:8"
imp = 0x0000000100003d30 (KCObjcBuild`-[LGPerson hobby])
}
(lldb) p $15.get(2).big()
(method_t::big) $19 = {
name = "setHobby:"
types = 0x0000000100003f9a "v24@0:8@16"
imp = 0x0000000100003d60 (KCObjcBuild`-[LGPerson setHobby:])
}
(lldb) p $15.get(3).big()
(method_t::big) $20 = {
name = "init"
types = 0x0000000100003f8a "@16@0:8"
imp = 0x0000000100003c10 (KCObjcBuild`-[LGPerson init])
}
(lldb) p $15.get(4).big()
(method_t::big) $21 = {
name = "name"
types = 0x0000000100003f8a "@16@0:8"
imp = 0x0000000100003cd0 (KCObjcBuild`-[LGPerson name])
}
(lldb) p $15.get(5).big()
(method_t::big) $22 = {
name = ".cxx_destruct"
types = 0x0000000100003f92 "v16@0:8"
imp = 0x0000000100003d90 (KCObjcBuild`-[LGPerson .cxx_destruct])
}
(lldb) p $15.get(6).big()
(method_t::big) $23 = {
name = "setName:"
types = 0x0000000100003f9a "v24@0:8@16"
imp = 0x0000000100003d00 (KCObjcBuild`-[LGPerson setName:])
}
(lldb) p LGPerson.class
(Class) $1 = 0x0000000100008410
(lldb) p/x 0x0000000100008410+0x20
(long) $2 = 0x0000000100008430
(lldb) p (class_data_bits_t *)0x0000000100008430
(class_data_bits_t *) $3 = 0x0000000100008430
(lldb) p $3->data()
(class_rw_t *) $4 = 0x0000000100e59cb0
(lldb) p *$4
(class_rw_t) $5 = {
flags = 2148007936
witness = 1
ro_or_rw_ext = {
std::__1::atomic
Value = 4295000448
}
}
firstSubclass = 0x00000001000083e8
nextSiblingClass = 0x00007ff85597aac8
}
(lldb) p $5.ro()
(const class_ro_t *) $6 = 0x0000000100008180
(lldb) p * $6
(const class_ro_t) $7 = {
flags = 388
instanceStart = 8
instanceSize = 32
reserved = 0
= {
ivarLayout = 0x0000000100003eb4 "\U00000003"
nonMetaclass = 0x0000000100003eb4
}
name = {
std::__1::atomic
Value = 0x0000000100003eb6 "LGPerson"
}
}
baseMethods = {
ptr = 0x00000001000081c8
}
baseProtocols = nil
ivars = 0x0000000100008278
weakIvarLayout = 0x0000000000000000
baseProperties = 0x00000001000082e0
_swiftMetadataInitializer_NEVER_USE = {}
}
(lldb) p $7.ivars
(const ivar_list_t *const) $8 = 0x0000000100008278
(lldb) p *$8
(const ivar_list_t) $9 = {
entsize_list_tt
}
(lldb) p $9.get(0)
(ivar_t) $10 = {
offset = 0x00000001000083a8
name = 0x0000000100003f31 "subject"
type = 0x0000000100003f78 "@\"NSString\""
alignment_raw = 3
size = 8
}
(lldb) p $9.get(1)
(ivar_t) $11 = {
offset = 0x00000001000083b0
name = 0x0000000100003f39 "_name"
type = 0x0000000100003f78 "@\"NSString\""
alignment_raw = 3
size = 8
}
(lldb) p $9.get(2)
(ivar_t) $12 = {
offset = 0x00000001000083b8
name = 0x0000000100003f3f "_hobby"
type = 0x0000000100003f78 "@\"NSString\""
alignment_raw = 3
size = 8
}
(lldb) p $9.get(3)
(lldb) p $9.get(0).big()
struct class_rw_ext_t {
DECLARE_AUTHED_PTR_TEMPLATE(class_ro_t)
class_ro_t_authed_ptr<constclass_ro_t> ro;
method_array_t methods;
property_array_tproperties;
protocol_array_t protocols;
char*demangledName;
uint32_tversion;
};
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_tflags;
uint16_twitness;
#if SUPPORT_INDEXED_ISA
uint16_t index;
#endif
explicit_atomic ro_or_rw_ext;
ClassfirstSubclass;
ClassnextSiblingClass;
struct class_data_bits_t {
friend objc_class;
class_rw_t* data() const {
return(class_rw_t*)(bits&FAST_DATA_MASK);
}
struct objc_class : objc_object {
objc_class(const objc_class&) = delete;
objc_class(objc_class&&) =delete;
void operator=(const objc_class&) = delete;
void operator=(objc_class&&) = delete;
// Class ISA;
Classsuperclass;
cache_tcache; // formerly cache pointer and vtable
class_data_bits_tbits; // class_rw_t * plus custom rr/alloc flags
class_rw_t *data() const {
returnbits.data();
}
23.类的结构里面为什么会有rw和ro以及rwe? 分值10分
(1)ro 属于 clean memory,在编译即确定的内存空间,只读,加载后不会改变内容的空间; (2)rw 属于 dirty memory,rw 是运行时结构,可读可写,可以向类中添加属性、方法等, 在运行时会改变的内存;
(3)rwe
相当于类的额外信息,因为在实际使用过程中,只有很少的类会真正的改变他 们的内容,所以为避免资源的消耗就有了 rwe; (4)运行时,如果需要动态向类中添加方法协议等,会创建 rwe,并将 ro 的数据优先 attache 到 rwe 中,在读取时会优先返回 rwe 的数据,如果 rwe 没有被初始化,则返回 ro 的数据。
rw 中包括 ro 和 rwe,其中 rw 是 dirtymemory,ro 是 clean memory;为了让 dirty memory 占用更少的空间,把 rw 中可变的部分抽取出来为 rwe;
clean memory
越多越好,dirty memory 越少越好,因为 iOS 系统底层虚内存机制的原 因,内存不足时会把一部分内存回收掉,后面需要再次使用时从硬盘中加载出来即 swap 机制,clean memory 是可以从硬盘中重新加载的内存,iOS 中的 macho 文件动态 库都属于此类行;dirty memory 是运行时产生的数据,这部分数据不能从硬盘中重新 加载所以必须一直占据内存,当系统物理内存紧张的时候,会回收掉 clean memory 内 存,如果 dirty memory 过大则直接会被回收掉;所以 clean memory 越多越好,dirty memory 越少越好;苹果对 rw、ro、rwe 进行这么细致的划分都是为了能更好更细致 的区别 cleanmemory 和 dirty memory;
24. cache 在什么时候开始扩容 , 为什么? 分值 10 分 1、一般情况下:如果当前方法 cache 之后,缓存的使用容量会超过总容量的 3/4,那么 此时就不会先插入,而是先触发扩容,扩容为原来的 2 倍,然后再插入本次的方法; 2、某些特殊预处理宏定义编译命令下,首次会存储满之后再开始扩容; 3、扩容时选用 3/4 作为负载因子是和 hash 表底层使用的链表以及红黑树的数据结 构有关,0.75 是最符合泊松分布概率计算得出的数值,在这个数值下哈希表的空间和 时间效率都是最高的;
25. objc_msgSend 为什么用汇编写 , objc_msgSend 是如何递归找到 imp? 分值
10分
(1)使用汇编响应速度快; (2)这个过程中使用了两个循环:
1.循环 1:通过前面获取的 mask,与要查找的_cmd 进行 hash 运算,获取下标,从而得到 _cmd 对应的 bucket 地址,然后进行向前平移查找,每次平移 16 个字节,如果找到对应 的 sel,则 cacheHit;当平移到 buckets 的首地址,依然没有查找到,则进入第二个循环; 2.循环 2:首先会获取末尾 bucket 的地址,同样采用向前查找的方式,向_cmd 对应的地 址进行平移查找。
一个类的类方法没有实现为什么可以调用 NSObject 同名对象方法 分值 10
分这里涉及到 isa 和 superclass 的走位以及方法查找逻辑。
首先类的
isa 指向元类,在方法快速查找时,会根据类的 isa 找到元类,元类中没有该方 法,就会走到 lookUpImpOrForward 慢速查找流程中。在慢速查找流程中,会进行 for 递归查找,根据 superclass 查找到元类的父类,也就是根元类,而根元类的 superclass 指 向了 NSObject,所以会调用到 NSObject 的同名对象方法。
ISA_MASK = 0x00007ffffffffff8ULL 那么这个 ISA_MASK 的算法意义是 什么? 分值10分
这个算法主要是为了得到 isa 中存储的 class 信息,大部分 isa 都是不纯的 isa,是一个 长达 64 个 byte 位的联合体位域数据,而存储 class 信息的部分只有其中的部分 byte 位置,剩下的位置存储了其他的信息;读取的时候必须把其他无效位置的 byte 位数
据给遮盖住,所以需要使用 isa_mask,任何数据与 isa_mask 进行按位与操作,都只会 保留 isamask 对应 byte 位置的数据,这个算法的意义就是遮盖,盖住不需要的地方, 这样就能让 isa 存储更多的信息;分值 10 分
怎么打开汇编查看流程,有什么好处 ? 分值 10 分
1、通过 Xcode-debug-debug workflow-always show disassembly 可以查看汇编; 2、查看汇编可以从更深层了解当前函数的汇编层面的执行,为 objc 源码分析提供信 息避免方向性错误,结合 memory read 可以更清楚的看到寄存器之间是如何互相配合 处理配合的;使用汇编查看流程,可以在不确定源码出处和执行流程的情况下,跟踪内 部代码,并可以找到出处!同时,结合下符号断点的方式,能够更清晰的跟踪源码实 现。
分值10分
19.x/4gx和p/x以及p*$0代表什么意思 分值10分
x/4gx : 输出一段内存地址,以 8 字节的形式输出 4 段; p/x :输出一个数据结构的首地址;
p *$0 : $0 为指向某一个数据空间的指针,而该指令输出的是该数据的数据结构。