《iOS底层原理文章汇总》
1.OC反汇编
objc_msgSend中存在两个参数(id,SEL),id类型实质上是一个结构体指针类型,SEL是一个选择器
x8寄存器中存放的是一个地址0x1021295b0,将x8寄存器中存放的地址值读取8个字节内容存放到x0寄存器中
再将x8寄存器中存放的一个地址0x1021295a0,读取8个字节内容存放到x1寄存器中
将x0,x1的值传入objc_msgSend方法中
(lldb) x 0x1021295b0
0x1021295b0: 98 96 12 02 01 00 00 00 d0 95 12 02 01 00 00 00 ................
0x1021295c0: 08 00 00 00 10 00 00 00 08 00 00 00 00 00 00 00 ................
(lldb) po 0x0102129698
Person
(lldb) x 0x1021295a0
0x1021295a0: 05 3d 2a 8b 01 00 00 00 00 00 00 00 00 00 00 00 .=*.............
0x1021295b0: 98 96 12 02 01 00 00 00 d0 95 12 02 01 00 00 00 ................
(lldb) po (SEL)0x018b2a3d05
"person"
(lldb) register read x0
x0 = 0x0000000102129698 (void *)0x000001a102129671
(lldb) register read x1
x1 = 0x000000018b2a3d05 "person"
(lldb)
通过objc_msgSend方法分析就能还原方法的调用
进入[Person person]方法后发现,系统的alloc和init在iOS11以后没有走objc_msgSend方法了,
根据系统的版本不同,而会有不同的优化,iOS11.0的系统中alloc不会调用objc_msgSend方法了,而init还会调用objc_msgSend方法,如下是iOS11.0系统真机
objc_msgSend方法中没有看到x0赋值,可以推测objc_alloc的返回值又赋值给了x0传入到了objc_msgSend方法,读取x0为init方法,读取x1为Person实例对象
最初的版本[[self alloc] init]会有两次消息发送,如iOS9.0
到了iOS11.0只有init会走消息发送
到了iOS13.0都不会走消息发送了
执行完[Person person]方法后,返回值存放在x0,是一个Person实例对象
会进入objc_storeStrong函数,OC中用strong修饰的对象都会调用这个函数,main中有个局部变量,相当于有个强引用在引用它Person *person = [Person person],这一句代码相当于让p销毁了,函数调用栈要平衡了
将x0存到x8指向的地址空间,x0为objc_msgSend的返回值&p
x8的值赋值给x0
0赋值给x1
(lldb) register read x0
x0 = 0x00000001c4039d60
(lldb) po 0x00000001c4039d60
传入objc_storeStrong方法,x0 = *location = &p;
obj = nil,
即p = nil,
id = prev = *location = person对象
*location = person对象 = obj = nil
objc_release(prev) = objc_release(person对象)回收堆空间
release p
栈平衡时回收栈空间释放p指针变量的地址
2.通过工具分析OC反汇编
将Product下生成的MachO文件直接拖入Hopper Disassembler v4中
双击OBJC_CLASS$_Person得到Person类的地址在1000096c8
双击Selector,person
MachO中分析汇编代码的时候根据地址能找到字符串,找到字符串后根据类型知道是什么,有类,类的引用,方法的名称,放在不同的位置代表不同的意义,hopper和MachOView都是根据特定的格式读取MachO文件
3.Block的反汇编
I.不引用外部变量的block
读取寄存器x0发现此block是一个全局block,全局静态block不引用外部变量,在编译器确定内存分配,存在于MachO文件中的可执行文件常量区
Block本质是结构体,isa指针8字节,为全局block、栈区block、堆区block,flags占用4个字节,reserved占用4字节,invoke中的内容是16个字节之后的内容,descriptor是底层问题描述
通过反汇编工具Hopper查看invoke中的内容,双击全局block,查看block结构
双击invoke,invoke中的内容都显示出来
descriptor在block的上方
II.引用外部变量的block
isa指针指向栈block
读取invoke,通过dis -s invoke内存地址读取到
hopper工具分析:双击invoke,双击descriptor查看
hopper查看流程图
流程图如下,if判断可以通过过汇编代码分析,但没有图分析显示分期的
查看伪代码:还原main函数