底层原理(一)对象的本质

问题抛出

  • 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占用多少内存?

相关文章
相关标签/搜索