1 OC 对象的本质(一个NSObject 对象占用的内存大小)

一 本质

OC 的面向对象都是基于C/C++ 的数据结构实现的

 

1 OC 对象的本质(一个NSObject 对象占用的内存大小)

 

1 OC 的对象和类主要是基于C/C++ 什么数据结构实现的?

结构体

 

2 clang 命令转换成c++ 代码

clang -rewrite-objc main.m -o main.cpp

以上的命令是不分平台进行编译的,main.cpp 代码有9万多行,如果指定用iphone 的话,用以下的命令,编译之后有3万多行代码

xcrun -sdk iphoneos clang  -arch arm64 -rewrite-objc main.m -o main.cpp

 

int main(int argc, const char * argv[]) {
 /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 

     NSObject *obj = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));
 }
 return 0;
}

 

4 NSObject 的本质

在c++代码中这是NSObject的实现

NSObject_IMPL 就是意味着 NSObject 的 Implementation

struct NSObject_IMPL {
    Class isa;
};

 

 

二 一个NSObject占用多少内存

1 按道理来讲,一个NSObject 本质就是一个结构体,其中的一个isa 指针在64位中是占据8个字节的

 

1 OC 对象的本质(一个NSObject 对象占用的内存大小)

 

实际是16个字节的

  NSObject *obj = [[NSObject alloc]init];

利用runtime 中的 class_getInstanceSize 获取大小

// 以下打印结果是8  ,以下代码的含义就是获取一个类的实例对象的大小

NSLog(@"%zu",class_getInstanceSize([NSObject class]));

导入 #import <malloc/malloc.h>

// 打印结果是 16,以下代码的含义是获取这个指针所指向内存的大小

NSLog(@"%zu",malloc_size((__bridge const void *)(obj)
         

所以这样来讲,如果问一个NSObject 对象占用多少内存,或者分配多少内存,就是16个字节

如果问 一个NSObject 对象实际是多大的内存,就是8个字节

 

2 为什么呢 ?

首先我们打开苹果开源的代码,找到代码一步步分析,找到最新的objc 代码下载分析

https://opensource.apple.com/tarballs/objc4/

 

1 OC 对象的本质(一个NSObject 对象占用的内存大小)

 

打开下载的工程文件 搜索allocWithZone(alloc本质调用的还是这个方法)

 

1 OC 对象的本质(一个NSObject 对象占用的内存大小)

 

以下是源代码调用的顺序

源码调用顺序

// Replaced by ObjectAlloc
+ (id)allocWithZone:(struct _NSZone *)zone {
    return _objc_rootAllocWithZone(self, (malloc_zone_t *)zone);
}



id
_objc_rootAllocWithZone(Class cls, malloc_zone_t *zone)
{
    id obj;

    if (fastpath(!zone)) {
        obj = class_createInstance(cls, 0);
    } else {
        obj = class_createInstanceFromZone(cls, 0, zone);
    }

    if (slowpath(!obj)) obj = _objc_callBadAllocHandler(cls);
    return obj;
}



id 
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone)
{
    void *bytes;
    size_t size;

    // Can't create something for nothing
    if (!cls) return nil;

    // Allocate and initialize
    size = cls->alignedInstanceSize() + extraBytes;

    // CF requires all objects be at least 16 bytes.
    if (size < 16) size = 16;

    if (zone) {
        bytes = malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
    } else {
        bytes = calloc(1, size);
    }

    return objc_constructInstance(cls, bytes);
}

// 内部有一个方法  instanceSize, 当内存不足16的时候,直接返回16

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

 

函数调用顺序。allocWithZone - >  objc_rootAllocWithZone -> class_createInstanceFromZone -> instanceSize(内部分配内存的方法)

说结论吧,内部调用了一个方法 instanceSize 方法 ,内存不足16,直接返回16 。

 

至于instance 方法内部又去调用了alignedInstanceSize 方法,这个方法其实返回的就是8,为什么呢? 因为isa 指针的大小就是8,说的很明白就是成员变量的大小


    // Class's ivar size rounded up to a pointer-size boundary.
    uint32_t alignedInstanceSize() const {
        return word_align(unalignedInstanceSize());
    }

 

3 如何在内存中看呢?

 

1 OC 对象的本质(一个NSObject 对象占用的内存大小)

获取 obj 的内存地址

在控制台中 打印

memory read 0x100664d00  这个地址,然后就是打印出,也是连续的16个字节

0x100664d00: 19 c1 02 95 ff ff 1d 00 00 00 00 00 00 00 00 00

  用以下明令也是可以的

x  0x100664d00

 

 

 

 

 

 

 

上一篇:OC中数组的valueForKeyPath函数应用


下一篇:OC中的isa指针