底層原理(一)對象的本質

問題拋出

  • objectiveC 的本質是什麼?c++

  • 能容納多種類型的結構是啥?面試

  • 一個對象佔多少字節objective-c

  • 一個OC對象在內存中是如何佈局的?佈局

Objective c 的本質是什麼?

衆所周知,OC 是 c ,c++ 的集合。到到下一層都會變成c,c++ 代碼的樣式。ui


能容納多種類型的結構是啥?

結構體,結構體可以容納不少類型的數據。spa


對象的本質是什麼?

咱們經過 clang -rewrite-objc main.m -o mian.cpp 看一下指針

main.m 的 codecode

LBPerson *person = [[LBPerson alloc] init];
        
NSLog(@"%zd",class_getInstanceSize([person class])); // 8
NSLog(@"%zd", malloc_size((__bridge const void *)person)); // 16
複製代碼

打印出來的結果分別是orm

爲何是這樣的呢?其實這裏涉及到c,c++ 寫的objc 代碼邏輯8字節對齊,以及malloc的策略,malloc 的策略是 16 字節對齊。那麼分配了內存16字節,只用了8字節,多了八字節。那麼已經用掉的八字節 以及 多出來的八字節分別是幹什麼用的呢?咱們埋下一個伏筆。咱們繼續看對象的本質是什麼?cdn

我在clang 事後的代碼裏面找到了這麼一段代碼:

恍然大悟,這是一個結構體那麼 NSObject_IMPL 是啥呢?我又找到了這段代碼。

✌️,咱們發現結構體裏面嵌套了一個結構體,那麼咱們大概清楚了,對象就是這麼一個結構體。我繼續看 Class isa:

那麼 Class 是什麼呢?

咱們回到main.m當中,點擊NSObject 進入:

@interface NSObject <NSObject> {
    Class isa  OBJC_ISA_AVAILABILITY;
}
複製代碼

那麼objc_class 又是什麼呢?我找到蘋果開源的代碼,發現是一個結構體,objc_object

發現 objc_object 也是一個結構體。那麼知道了,Class 是結構體指針。由此咱們知道了,一個對象的第一個屬性是一個isa,isa 是一個結構體指針。而結構體的第一個成員是isa_t。那麼指針指向對象的首地址就是isa_t 的首地址。一個指針的大小是8個字節。那麼咱們的最後一個問題也迎刃而解了,內存分佈結構體也差很少知道了。

可是還有一個問題,這個對象的大小到底是多少呢?這就要看怎麼問了,若是說一不二個對象的實際大小,那麼就是8,若是說是一個對象分配的內存空間,那麼就是至少16 。咱們其實上面的class_getinstanceSize 是求的實際用了多大的空間。沒有用掉的空間實際上也是這個對象的所擁有空間。通常我會回答 是16 字節。

咱們來具體看一下代碼咱們就清楚爲何了。

蘋果開源代碼中有這麼一段代碼:

咱們建立對象的過程當中有這麼一段代碼:
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone, 
                              bool cxxConstruct = true, 
                              size_t *outAllocatedSize = nil)
{
    if (!cls) return nil;

    assert(cls->isRealized());

    // Read class's info bits all at once for performance
    bool hasCxxCtor = cls->hasCxxCtor();
    bool hasCxxDtor = cls->hasCxxDtor();
    bool fast = cls->canAllocNonpointer();
	// 這裏就是得到空間大小的代碼
    size_t size = cls->instanceSize(extraBytes);
    if (outAllocatedSize) *outAllocatedSize = size;

    id obj;
    if (!zone  &&  fast) {
        obj = (id)calloc(1, size); // 建立空間
        if (!obj) return nil;
        obj->initInstanceIsa(cls, hasCxxDtor); // 存儲數據
    } 
    else {
        if (zone) {
            obj = (id)malloc_zone_calloc ((malloc_zone_t *)zone, 1, size);
        } else {
            obj = (id)calloc(1, size);
        }
        if (!obj) return nil;

        // Use raw pointer isa on the assumption that they might be 
        // doing something weird with the zone or RR.
        obj->initIsa(cls);
    }

    if (cxxConstruct && hasCxxCtor) {
        obj = _objc_constructOrFree(obj, cls);
    }

    return obj;
}
複製代碼

第一個初始化

size_t instanceSize(size_t extraBytes) {
        size_t size = alignedInstanceSize() + extraBytes; // 這裏實際上是字節對齊,我會在下一節進行分析。
        // CF requires all objects be at least 16 bytes.
        if (size < 16) size = 16;
        return size;
}
複製代碼

咱們也從側面驗證了一個對象若是沒有進行屬性分配**,會分配16 字節的大小。儘管,空間不會所有用完 。**

綜上:對象的大小通常說16 字節。


咱們回過頭來看class_getInstanceSize 的原理
size_t class_getInstanceSize(Class cls)
{
    if (!cls) return 0;
    return cls->alignedInstanceSize();
}
複製代碼
uint32_t alignedInstanceSize() {
    return word_align(unalignedInstanceSize());
}
複製代碼

咱們能夠看到,其實最終調用的都是 字節對齊對應的大小。

知識點補充:

  • 一個16進制位須要4個二進制位表示,兩個16進制位,須要八個二進制位表示。那麼兩個16進制位表明一個字節。

關於這裏 涉及到 class_data_bits_t 裏面的 class_rw_t -> class_ro_t 。之後分析。

面試題

一個NSObject佔用多少內存?

相關文章
相關標籤/搜索