一、OC对象初始化
AppleSVP *a1 = [AppleSVP alloc];
AppleSVP *a2 = [a1 init];
AppleSVP *a3 = [a1 init];
打印指针:
打印指针
JONYLog(@"%@--%p",a1,a1);
JONYLog(@"%@--%p",a2,a2);
JONYLog(@"%@--%p",a3,a3);
结果:
Jony : alloc init 开辟内存探索
<AppleSVP: 0x600000bbc270>--0x600000bbc270
<AppleSVP: 0x600000bbc270>--0x600000bbc270
<AppleSVP: 0x600000bbc270>--0x600000bbc270
结论: alloc开辟内存空间后,init并没有开辟新的内存空间,地址一抹抹一样样。 再打印指针本身的地址:
JONYLog(@"%@--%p--%p",a1,a1,&a1);
JONYLog(@"%@--%p--%p",a2,a2,&a2);
JONYLog(@"%@--%p--%p",a3,a3,&a3);
打印结果为:
Jony : alloc init 开辟内存探索
<AppleSVP: 0x60000108c190>--0x60000108c190--0x16f283f08
<AppleSVP: 0x60000108c190>--0x60000108c190--0x16f283f00
<AppleSVP: 0x60000108c190>--0x60000108c190--0x16f283ef8
结论: 指针自身地址并不相同,并且相隔为8。
二、符号断点调试
第一种:添加符号断点objc_alloc
启动重跑会来到 objc_alloc
第二种:对象alloc处添加断点
打开汇编调试代码
第三种:通过已知符号独断添加alloc符号断点,确定未知
可得:[NSObject alloc]
三、底层源码调试
官网下载objc源码:
开始源码调试,搜索alloc方法,定位到NSObject.mm混编代码:
定位代码:
+ (id)alloc {
return _objc_rootAlloc(self);
}
点进去:
id _objc_rootAlloc(Class cls)
{
return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
再点进去,核心方法:
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
#if __OBJC2__
if (slowpath(checkNil && !cls)) return nil;
if (fastpath(!cls->ISA()->hasCustomAWZ())) {
return _objc_rootAllocWithZone(cls, nil);
}
#endif
// N o shortcuts available.
if (allocWithZone) {
return ((id(*)(id, SEL, struct _NSZone *))objc_msgSend)(cls, @selector(allocWithZone:), nil);
}
return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(alloc));
}
此时究竟是跑_objc_rootAllocWithZone方法还是objc_msgSend方法,现在还不得而知
先给代码打上断点
再添加3个断点
点击来到汇编:
可以看到,先走了_objc_rootAllocWithZone,再走objc_msgSend
重新走一遍:
首先来到_objc_rootAlloc
再来到_objc_rootAllocWithZone
并没有经过callAlloc断点
原因:
编译器优化
打个断点,重跑一次
添加断点
点进去源码,再添加断点
跑一下,打一下当前对象地址
(lldb) p obj
(id) $1 = 0x00000001000083b0
(lldb)
再点击往下走
此时,p当前的obj对象
(lldb) p obj
(id) $1 = 0x00000001000083b0
(lldb) p obj
(id) $2 = 0x0000000100e04080
(lldb)
结论:
发现对象的地址变了,此时重新分配了新的内存空间,上面的内存数据是脏内存。
在isaInit前面添加断点:
往下走,断点已经走完isaInit
此时再p一下obj,发现已经绑定了类:
(lldb) p obj
(id) $1 = 0x00000001000083b0
(lldb) p obj
(id) $2 = 0x0000000100e04080
(lldb) p obj
(ApplePerson *) $3 = 0x0000000100e04080
(lldb)
结论: 此时可以看到,p obj对象已经绑定了ApplePerson
四、lldb调试
重跑一下工程:
添加断点,并跑到断点:
此时,extraBytes是0
点进instanceSize方法,添加断点
可以看到,如果size小于16就等于16,还有字节对齐方法alignedInstanceSize(),点进去
uint32_t alignedInstanceSize() const {
return word_align(unalignedInstanceSize());
}
再点进去,字节对齐算法:
static inline uint32_t word_align(uint32_t x) {
return (x + WORD_MASK) & ~WORD_MASK;
}
8字节对齐计算
(8 + 7) & ~ 7 = 15 & ~ 7 = 8字节对齐 取8的整数
0000 1111
1111 1000 (7取反)
0000 1000 结果= 8
实际就是:右移3位,再左移3位
(8 + 7) >>3 再 <<3
0000 0111 (7)
查看内存
x p一下
(lldb) x p
0x1011aa940: b1 83 00 00 a1 21 00 00 00 00 00 00 00 00 00 00 .....!..........
0x1011aa950: 2d 5b 4e 53 54 6f 6f 6c 62 61 72 43 6f 6c 6c 65 -[NSToolbarColle
(lldb)
因为是ios是小端模式,内存从左往右读,可以通过地址查看内存情况:
p一下地址
(lldb) p 0x21a1000083b1
(long) $1 = 36975373484977
(lldb)
拼上isa_mask po一下
(lldb) x p
0x1011aa940: b1 83 00 00 a1 21 00 00 00 00 00 00 00 00 00 00 .....!..........
0x1011aa950: 2d 5b 4e 53 54 6f 6f 6c 62 61 72 43 6f 6c 6c 65 -[NSToolbarColle
(lldb) p 0x21a1000083b1
(long) $1 = 36975373484977
(lldb) po 0x21a1000083b1 & 0x0000000ffffffff8
ApplePerson
NSLog添加断点调试
ApplePerson *p = [ApplePerson alloc] ;
p.name = @"kc";
p.nickName = @"细细";
p.age = 18;
p.height = 10;
NSLog(@"%@",p);
打印内存:
(lldb) x/6gx p
0x1011c46a0: 0x000021a100008499 0x0000000a00000012
0x1011c46b0: 0x0000000100004008 0x0000000100004028
0x1011c46c0: 0x000000020cc639d8 0x0000000000000000
(lldb) po 0x0000000a
10
(lldb) po 0x00000012
18
(lldb) po 0x0000000100004008
kc
(lldb) po 0x0000000100004028
细细
(lldb) po 0x000000020cc639d8
8804252120
(lldb) po 0x0000000000000000
<nil>
(lldb)
可以看到10和18公用一个8字节地址,这就是字节对齐
通过上面:最终我们绘制出对象的alloc和init的流程图