iOS底层原理:alloc探索

370 阅读3分钟
一、问题思考:
  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流程图

alloc流程图.png

备注: 源码 objc4-818.2;

未完待续