OC对象的本质

一、本质

  1. OC对象的本质是C和C++中的结构体

  2. OC代码转C++代码:clang -rewrite-objc main.m -o main.cpp

    • 转化成什么平台的代码,不同平台支持的代码不一样。例如:Windows、MacOS,iOS。
    • iOS:模拟器(i386/x86_64)、32bit(armv7)、64bit(arm64)
    • 命令:xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main_arm64.cpp
  3. Class是一个指向结构体的指针。
    typedef struct objc_class *Class 指针在64位机器中占8个字节
    sizeof():是针对类型的,得到一个类型占多少内存。sizeof不是函数,是运算符,编译过后就变成了常数,不存在函数调用(跟宏定义有点类似)。如:sizeof(int)、sizeof(double)、sizeof(struct Student_IMPL)

NSObject *obj = [[NSObject alloc] init];
// 获取NSObject类的实例对象成员变量所占用的大小 >> 8,返回结构体内存对齐过后的成员变量大小
NSLog(@"%zd", class_getInstanceSize([NSObject class]));
// 获取obj所指向内存的大小 >> 16
NSLog(@"%zd", malloc_size((__bridge const void *)obj));
sizeof(struct Student_IMPL);   // 结构体Student_IMPL的大小
sizeof(obj) --> 8   // obj是个指针,占8个字节
sizeof(int) --> 4    // int占4个字节
int a = 10;
sizeof(a) --> 4
// May be unaligned depending on class's ivars.
uint32_t unalignedInstanceSize() {
    assert(isRealized());
    return data()->ro->instanceSize;
}

// Class's ivar size rounded up to a pointer-size boundary.
uint32_t alignedInstanceSize() {
    // 返回内存对齐过后的大小(align对齐)
    return word_align(unalignedInstanceSize());
}

// class_getInstanceSize的实现
size_t class_getInstanceSize(Class cls)
{
    if (!cls) return 0;
    return cls->alignedInstanceSize();
}

A、题:一个NSObject对象占用多少内存?

答:1、系统分配了16个字节给NSObject对象(通过malloc_size函数获得) 2、 但NSObject对象内部只使用了8个字节的空间(64位环境下,通过class_getInstanceSize函数获得)


注意:Class isa只占8个字节,但是[NSObject alloc]会分配16个字节。OC对象分配的内存最少16个字节

// calloc函数分配内存大小的时候调用的size函数,所有OC对象最少会分配16个字节
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;
}
struct Student_IMPL {
//    struct NSObject_IMPL NSObject_IVARS;
    Class isa;   // 8字节
    int _no;  // 4字节
    int _age;  // 4字节
};

s->_no = 4; 通过指针直接访问成员变量
Student:NSObject 占16个字节,8 + 4 + 4
结构体内存对齐:结构体的最终大小必须是最大成员大小的倍数。

struct NSObject_IMPL {
    Class isa;
};

struct Person_IMPL {
//    struct NSObject_IMPL NSObject_IVARS;
    Class isa;    // 8个字节
    int _age;     // 4个字节
};  // 16个字节  内存对齐:结构体的最终大小必须是最大成员大小的倍数


struct Student_IMPL {
//    struct Person_IMPL Person_IVARS;
    Class isa;   // 16个字节
    int _no;     // 4个字节
};   // 16个字节  Class isa 里面实际用到的只有12个字节,下面的4个字节直接使用空地址

二、属性

对于成员变量的属性,只会在该类的结构体中申明对应的成员变量,不会有对应的get,set方法。

方法是不会放在实例对象里面的,因为实例对象可以创建很多份,一一不同,但是方法只需要保存一份即可。方法保存在类对象的方法列表里面

实例方法保存在类对象的方法列表里面,类方法保存在元类对象的方法列表里面(一个类的类对象和元类对象有且只有一份)。


B、题:创建一个实例对象,至少需要多少内存?
答:
1、#import   
2、class_getInstanceSize([NSObject class]);
3、sizeof(struct NSObject_IMPL);
故:至少需要8个字节

C、题:创建一个实例对象,实际分配多少内存?
答:
1、#import   
2、malloc_size((__bridge const void *)obj);
故:实际分配了16个字节,分配的字节数是16的倍数

// calloc分配内存的时候遵守的内存对齐
#define NANO_MAX_SIZE  256  /** Buckets sized {16, 32, 48, 64, 80, 96, 112, ...}*/

我们可以总结内存对齐为两个原则:
原则 1. 前面的地址必须是后面的地址正数倍,不是就补齐。
原则 2. 整个Struct的地址必须是最大字节的整数倍。

底层实现:
// NSObject:
struct NSObject_IMPL {
    Class isa;
};

// Class
struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

    class_rw_t *data() { 
        return bits.data();
    }
    void setData(class_rw_t *newData) {
        bits.setData(newData);
    }
    ...
}
struct objc_object {
private:
    isa_t isa;

public:

    // ISA() assumes this is NOT a tagged pointer object
    Class ISA();

    // getIsa() allows this to be a tagged pointer object
    Class getIsa();
    ...
}

struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint32_t version;

    const class_ro_t *ro;

    method_array_t methods;
    property_array_t properties;
    protocol_array_t protocols;

    Class firstSubclass;
    Class nextSiblingClass;
    ...
}

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
#ifdef __LP64__
    uint32_t reserved;
#endif

    const uint8_t * ivarLayout;
    
    const char * name;
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;

    method_list_t *baseMethods() const {
        return baseMethodList;
    }
};

你可能感兴趣的:(OC对象的本质)