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

一、底層研究的三種方法(查找底層源碼出處)

  1. 下符號斷點
    斷點斷住[LGPerson alloc];


    1624431409030.png

我們要研究alloc方法所以直接下一個alloc的符號斷點(第二種方法詳細講了下符號斷點的方法)、然后點擊continue


1624431595445.png

這時候會進入alloc的符號斷點,可以看到alloc方法是在libobjc.A.dylib庫中
ps:也可以一開始就下alloc的符號斷點,但是要先設(shè)置成Disable 等到了[LGPerson alloc];斷點之后再把alloc的符號斷點設(shè)置成Enable因為系統(tǒng)也會調(diào)用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. 匯編跟流程查看實際調(diào)用的符號下符號斷點


    1624432052303.png

設(shè)置Xcode ->Debug ->Debug Workflow->Always Show Disaassemdly顯示匯編代碼


1624432746381.png

可以看到實際調(diào)用的是objc_alloc符號 接下來下個對應(yīng)的符號斷點,就可以斷點objc_alloc可以看到源碼的出處

源碼下載地址

蘋果開源源碼匯總: https://opensource.apple.com
這個地址?的更直接 https://opensource.apple.com/tarballs/

1624433023683.png

1624433441168.png

進入蘋果開源網(wǎng)站點擊macOS下的版本能看到蘋果開源的源碼庫 搜索objc下載對應(yīng)的源碼

二、探究alloc源碼


1624436326622.png

可以看到p1、p2、p3指向同一個對象(指向的內(nèi)存地址也是一樣的)三個指針自身的地址不同

PS:p1、p2、p3是在??臻g里的三個指針、他們的內(nèi)存不同、他們指向堆空間里同一個地址LGPerson對象

接下來通過源碼探究alloc 和 init做了些啥

1624438514478.png

在源碼里搜索alloc的實現(xiàn)可以看到是在NSObject.mm(OC、C、C++混編)里調(diào)用了_objc_rootAlloc方法,一路找下去


1624438688956.png

_objc_rootAlloc調(diào)用了callAlloc


1624438702426.png

callAlloc里通過cls->ISA()->hasCustomAWZ()當前class是否有自定義的allocWithZone,如果沒有自定義會調(diào)用_objc_rootAllocWithZone


1624439625387.png

而_objc_rootAllocWithZone最終會調(diào)用_class_createInstanceFromZone這個方法里最終生成對象,這里先理清楚流程后面會具體分析這個方法


1624439645268.png

callAlloc里如果自定義的allocWithZone,會判斷當前類是否可以快速alloc如果沒有會通過objc_msgSend調(diào)用allocWithZone:方法
如果可以快速alloc會通過objc_msgSend調(diào)用alloc方法
根據(jù)這個流程得到下面的流程圖


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構(gòu)造方法的實現(xiàn)。

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析構(gòu)方法的實現(xiàn)。

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結(jié)構(gòu)體中,然后經(jīng)過對齊最后返回(對象的內(nèi)存大小8字節(jié)對齊,這樣可以提高cpu效率)。

注意:Core Foundation 需要所有的對象的大小都必須大于或等于 16 字節(jié)。

在獲取對象大小之后,直接調(diào)用calloc函數(shù)就可以為對象分配內(nèi)存空間了。

關(guān)于calloc函數(shù)

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()函數(shù)連續(xù)地為count對象分配足夠的空間,這些對象是內(nèi)存的大小字節(jié),并返回一個指向所分配內(nèi)存的指針。分配的內(nèi)存充滿了值為零的字節(jié)。

申請完內(nèi)存空間之后,還需要再初始化Isa指針。

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

初始化Isa指針有這上面兩個函數(shù)。

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);
}

從上述源碼中,我們也能看出,最終都是調(diào)用了initIsa函數(shù),只不過入?yún)⒉煌?/p>

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結(jié)構(gòu)體初始化的過程。
初始化之后obj被ruturn給外界使用alloc流程結(jié)束

待補充內(nèi)容:
1、編譯器優(yōu)化
2、對象的字節(jié)對齊
3、內(nèi)存對齊
4、對象大小的影響因素

參考資料:OC對象的一生

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容