一、问题思考:
QZPerson *p1 = [QZPerson alloc];
QZPerson *p2 = [p1 init];
QZPerson *p3 = [p1 init];
NSLog(@"%@-内存地址%p-指针的地址%p",p1,p1,&p1);
NSLog(@"%@-内存地址%p-指针的地址%p",p2,p2,&p2);
NSLog(@"%@-内存地址%p-指针的地址%p",p3,p3,&p3);
注释:p1打印的是:对象的内存首地址;&p1打印的是:指针p1本身的内存首地址。
打印结果为:
<QZPerson: 0x6000025c0ac0>-内存地址0x6000025c0ac0-指针的地址0x7ffee67ae0c8
<QZPerson: 0x6000025c0ac0>-内存地址0x6000025c0ac0-指针的地址0x7ffee67ae0c0
<QZPerson: 0x6000025c0ac0>-内存地址0x6000025c0ac0-指针的地址0x7ffee67ae0b8
结论:
- 1、三个对象的,内存地址空间相同。说明对象的空间开辟来自于alloc。
- 2、栈内存是连续的。指针地址每个相差8字节。
思考:alloc开辟内存,那么alloc的具体流程是怎样的呢?
二、底层探索的三个方法
1、下符号断点的形式直接跟流程
1)Symbolic BreakPoint 加断点 alloc
因为有很多alloc,所以先断点到所要定位到代码行,然后再打开断点alloc
libobjc.A.dylib`+[NSObject alloc]:
2)点击 step info 会进入
libobjc.A.dylib`_objc_rootAlloc:
2、断点 ,通过摁住control - step into
1)通过断点alloc 然后 control - step into 进入可以定位到 objc_alloc
eg:001-alloc&init探索`objc_alloc:
2)通过打断点 objc_alloc 可以定位到 libobjc.A.dylib`objc_alloc:
3)然后去查源码 libobjc.A.dylib
3、汇编查看跟流程,查看源码出处
1)断点到 alloc代码行
2)打开汇编流程Debug -> Debug Workflow -> Always Show Disassembly
看到如下:
0x10a98bd7e <+158>: movq 0x76d3(%rip), %rcx ; (void *)0x000000010a993548: QZPerson
0x10a98bd85 <+165>: movq %rcx, %rdi
0x10a98bd88 <+168>: callq 0x10a98c3a6 ; symbol stub for: objc_alloc
3)摁住control - step into 可以定位到 objc_alloc
eg:001-alloc&init探索`objc_alloc:
4)通过断点 objc_alloc 可以定位到 libobjc.A.dylib
苹果开源的库
苹果开源源码汇总: opensource.apple.com
这个地址⽤的更直接 opensource.apple.com/tarballs/
三、alloc过程的探索研究
alloc
+ (id)alloc {
return _objc_rootAlloc(self);
}
方法_objc_rootAlloc
_objc_rootAlloc(Class cls)
{
return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
方法 callAlloc
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
#if __OBJC2__ //判断是不是 objc2.0版本
//其实将fastpath和slowpath去掉是完全不影响任何功能,写上是告诉编译器对代码进行优化
if (slowpath(checkNil && !cls)) return nil;
//判断该类是否实现自自定义的 +allocWithZone,没有则进入if条件句
if (fastpath(!cls->ISA()->hasCustomAWZ())) {
return _objc_rootAllocWithZone(cls, nil);
}
#endif
// No 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
id
_objc_rootAllocWithZone(Class cls, malloc_zone_t *zone __unused)
{
// allocWithZone under __OBJC2__ ignores the zone parameter
return _class_createInstanceFromZone(cls, 0, nil,
OBJECT_CONSTRUCT_CALL_BADALLOC);
}
核心流程 _class_createInstanceFromZone
static ALWAYS_INLINE id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
int construct_flags = OBJECT_CONSTRUCT_NONE,
bool cxxConstruct = true,
size_t *outAllocatedSize = nil)
{
ASSERT(cls->isRealized());
// Read class's info bits all at once for performance
bool hasCxxCtor = cxxConstruct && cls->hasCxxCtor();
bool hasCxxDtor = cls->hasCxxDtor();
bool fast = cls->canAllocNonpointer();
size_t size;
size = cls->instanceSize(extraBytes);
if (outAllocatedSize) *outAllocatedSize = size;
id obj;
if (zone) {
obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
} else {
obj = (id)calloc(1, size); // 开辟内存空间
}
if (slowpath(!obj)) {
if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
return _objc_callBadAllocHandler(cls);
}
return nil;
}
if (!zone && fast) {
// 初始化 isa指针 和cls 关联起来
obj->initInstanceIsa(cls, hasCxxDtor);// 关联内存空间到相应的类
} else {
// Use raw pointer isa on the assumption that they might be
// doing something weird with the zone or RR.
obj->initIsa(cls);
}
if (fastpath(!hasCxxCtor)) {
return obj;
}
construct_flags |= OBJECT_CONSTRUCT_FREE_ONFAILURE;
return object_cxxConstructFromClass(obj, cls, construct_flags);
}
从流程图和源代码可以看出 _class_createInstanceFromZone 方法中有核心三个方法需要实现
- cls->instanceSize : 计算内存大小 8字节 对齐 内存对齐参见下一章
- (id)calloc(1, size) : 开辟内存,返回地址指针
- obj->initInstanceIsa :初始化指针,和类关联起来
instanceSize
uint32_t alignedInstanceSize() const {
return word_align(unalignedInstanceSize());
}
inline size_t instanceSize(size_t extraBytes) const {
if (fastpath(cache.hasFastInstanceSize(extraBytes))) {
return cache.fastInstanceSize(extraBytes);
}
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16; // 内存大小至少是16个字节
return size;
}
objc 源码:--> word_align 8字节对齐: (x + 7) & (7取反)
#ifdef __LP64__
# define WORD_SHIFT 3UL
# define WORD_MASK 7UL
# define WORD_BITS 64
#else
# define WORD_SHIFT 2UL
# define WORD_MASK 3UL
# define WORD_BITS 32
#endif
static inline uint32_t word_align(uint32_t x) {
return (x + WORD_MASK) & ~WORD_MASK; // 8字节对齐
}
static inline size_t word_align(size_t x) {
return (x + WORD_MASK) & ~WORD_MASK; // 8字节对齐
}
static inline size_t align16(size_t x) {
return (x + size_t(15)) & ~size_t(15); // 16字节对齐
}
四、画出alloc流程图
备注: 源码 objc4-818.2;
未完待续