iOS底层原理探究01-alloc底层原理

一、底层研究的三种方法(查找底层源码出处)

  1. 下符号断点
    断点断住[LGPerson alloc];


    1624431409030.png

我们要研究alloc方法所以直接下一个alloc的符号断点(第二种方法详细讲了下符号断点的方法)、然后点击continue


1624431595445.png

这时候会进入alloc的符号断点,可以看到alloc方法是在libobjc.A.dylib库中
ps:也可以一开始就下alloc的符号断点,但是要先设置成Disable 等到了[LGPerson alloc];断点之后再把alloc的符号断点设置成Enable因为系统也会调用alloc方法我们要避免干扰准确的断住LGPerson的alloc

  1. 按住control + step into进入源码出处


    1624430823944.png

下断点断住[LGPerson alloc]、然后按住control + step into进入源码


1624430880268.png

可以看到底层实际上是进入了objc_alloc方法里


1624430955592.png
1624430998542.png

添加一个objc_alloc的符号断点,然后点击continue


1624431062056.png

可以看到objc_alloc方法是在libobjc.A.dylib库中

  1. 汇编跟流程查看实际调用的符号下符号断点


    1624432052303.png

设置Xcode ->Debug ->Debug Workflow->Always Show Disaassemdly显示汇编代码


1624432746381.png

可以看到实际调用的是objc_alloc符号 接下来下个对应的符号断点,就可以断点objc_alloc可以看到源码的出处

源码下载地址

苹果开源源码汇总: https://opensource.apple.com
这个地址⽤的更直接 https://opensource.apple.com/tarballs/

1624433023683.png

1624433441168.png

进入苹果开源网站点击macOS下的版本能看到苹果开源的源码库 搜索objc下载对应的源码

二、探究alloc源码


1624436326622.png

可以看到p1、p2、p3指向同一个对象(指向的内存地址也是一样的)三个指针自身的地址不同

PS:p1、p2、p3是在栈空间里的三个指针、他们的内存不同、他们指向堆空间里同一个地址LGPerson对象

接下来通过源码探究alloc 和 init做了些啥

1624438514478.png

在源码里搜索alloc的实现可以看到是在NSObject.mm(OC、C、C++混编)里调用了_objc_rootAlloc方法,一路找下去


1624438688956.png

_objc_rootAlloc调用了callAlloc


1624438702426.png

callAlloc里通过cls->ISA()->hasCustomAWZ()当前class是否有自定义的allocWithZone,如果没有自定义会调用_objc_rootAllocWithZone


1624439625387.png

而_objc_rootAllocWithZone最终会调用_class_createInstanceFromZone这个方法里最终生成对象,这里先理清楚流程后面会具体分析这个方法


1624439645268.png

callAlloc里如果自定义的allocWithZone,会判断当前类是否可以快速alloc如果没有会通过objc_msgSend调用allocWithZone:方法
如果可以快速alloc会通过objc_msgSend调用alloc方法
根据这个流程得到下面的流程图


alloc流程 (1).png

可以看到最终都是通过_class_createInstanceFromZone方法生成的对象接下来就来看下这个方法
/***********************************************************************

  • class_createInstance
  • fixme
  • Locking: none
  • Note: this function has been carefully written so that the fastpath
  • takes no branch.
    **********************************************************************/
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) {
        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);
}

ctor 和 dtor 分别是什么呢?

bool hasCxxCtor() {
        ASSERT(isRealized());
        return cache.getBit(FAST_CACHE_HAS_CXX_CTOR);
    }
bool hasCxxCtor() {
        ASSERT(isRealized());
        return bits.data()->flags & RW_HAS_CXX_CTOR;
    }

ctor是判断当前class或者superclass 是否有.cxx_construct构造方法的实现。

bool hasCxxDtor() {
    ASSERT(isRealized());
    return cache.getBit(FAST_CACHE_HAS_CXX_DTOR);
}
bool hasCxxDtor() {
    ASSERT(isRealized());
    return bits.data()->flags & RW_HAS_CXX_DTOR;
}

dtor是判断判断当前class或者superclass 是否有.cxx_destruct析构方法的实现。

uint32_t unalignedInstanceSize() const {
        ASSERT(isRealized());
        return data()->ro()->instanceSize;
    }
    // Class's ivar size rounded up to a pointer-size boundary.
    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;  
        return size;
    }

实例大小 instanceSize会存储在类的 isa_t结构体中,然后经过对齐最后返回(对象的内存大小8字节对齐,这样可以提高cpu效率)。

注意:Core Foundation 需要所有的对象的大小都必须大于或等于 16 字节。

在获取对象大小之后,直接调用calloc函数就可以为对象分配内存空间了。

关于calloc函数

The calloc( ) function contiguously allocates enough space for count objects that are size bytes of memory each and returns a pointer to the allocated memory. The allocated memory is filled with bytes of value zero. // calloc()函数连续地为count对象分配足够的空间,这些对象是内存的大小字节,并返回一个指向所分配内存的指针。分配的内存充满了值为零的字节。

申请完内存空间之后,还需要再初始化Isa指针。

obj->initInstanceIsa(cls, hasCxxDtor);
obj->initIsa(cls);

初始化Isa指针有这上面两个函数。

objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
    ASSERT(!cls->instancesRequireRawIsa());
    ASSERT(hasCxxDtor == cls->hasCxxDtor());

    initIsa(cls, true, hasCxxDtor);
}

objc_object::initIsa(Class cls)
{
    initIsa(cls, false, false);
}

从上述源码中,我们也能看出,最终都是调用了initIsa函数,只不过入参不同。

objc_object::initIsa(Class cls, bool nonpointer, UNUSED_WITHOUT_INDEXED_ISA_AND_DTOR_BIT bool hasCxxDtor)
{ 
    ASSERT(!isTaggedPointer()); 
    
    isa_t newisa(0);

    if (!nonpointer) {
        newisa.setClass(cls, this);
    } else {
        ASSERT(!DisableNonpointerIsa);
        ASSERT(!cls->instancesRequireRawIsa());


#if SUPPORT_INDEXED_ISA
        ASSERT(cls->classArrayIndex() > 0);
        newisa.bits = ISA_INDEX_MAGIC_VALUE;
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.nonpointer is part of ISA_MAGIC_VALUE
        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
        newisa.bits = ISA_MAGIC_VALUE;
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.nonpointer is part of ISA_MAGIC_VALUE
#   if ISA_HAS_CXX_DTOR_BIT
        newisa.has_cxx_dtor = hasCxxDtor;
#   endif
        newisa.setClass(cls, this);
#endif
        newisa.extra_rc = 1;
    }

    // This write must be performed in a single store in some cases
    // (for example when realizing a class because other threads
    // may simultaneously try to use the class).
    // fixme use atomics here to guarantee single-store and to
    // guarantee memory order w.r.t. the class index table
    // ...but not too atomic because we don't want to hurt instantiation
    isa = newisa;
}

初始化的过程就是对isa_t结构体初始化的过程。
初始化之后obj被ruturn给外界使用alloc流程结束

待补充内容:
1、编译器优化
2、对象的字节对齐
3、内存对齐
4、对象大小的影响因素

参考资料:OC对象的一生

你可能感兴趣的:(iOS底层原理探究01-alloc底层原理)