OC实例对象占据空间大小

2018-10-27  本文已影响22人  川少叶

OC中的类是基于C/C++的结构体实现的,通过如下命令可以将OC代码转换成C/C++代码

xcrun  -sdk  iphoneos  clang  -arch  arm64  -rewrite-objc  OC源文件  -o  输出的CPP文件

从最简单的NSObject入手,使用以上命令转换如下的OC代码:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSObject *object = [[NSObject alloc] init];
    }
    return 0;
}

在转换的cpp代码中,找到NSObject的结构体定义

typedef struct objc_class *Class;
 struct NSObject_IMPL {
    Class isa;
 };

对比OC中NSObject的定义

@interface NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
    Class isa  OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}
@end

根据NSObject的结构体定义,一个NSObject只包含一个Class指针,在64位机器上,占据的空间大小应该是8个字节,其实质是成员变量占据的空间大小

那NSObject实例对象占据的空间是多少,请看下面的代码。答案是16。

void theSizeOfNSObject() {
    NSObject *object = [[NSObject alloc] init];
    
    // 根据objc源码,class_getInstanceSize表示成员变量占据的空间大小。
    NSLog(@"NSObject class_getInstanceSize: %zd", class_getInstanceSize([NSObject class]));
    // 根据objc源码,malloc_size表示该实例对象占据的空间大小,最小是16
    NSLog(@"NSObject alloc size: %zd", malloc_size((__bridge const void *)object));
}

查看Objc runtime的源码,找到class_getInstanceSizeallocWithZone的实现。

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

// Class's ivar size rounded up to a pointer-size boundary.
// 注意会字节对齐
uint32_t alignedInstanceSize() {
    return word_align(unalignedInstanceSize());
}

// allocWithZone方法,会调用该方法
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;
}

定义两个稍复杂的类。其中需要注意的是,Student中属性score,被转换成结构体中_scroe

@interface Person : NSObject {
    @public
    int _age;
}
@end

@interface Student : Person {
    @public
    int _num;
}
@property (nonatomic, assign) int score;

@end

通过以上命令生成cpp文件,Person和Student的结构体如下:

// Person转换成的结构体
struct Person_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    int _age;
};

// Student转换成的结构体
struct Student_IMPL {
    struct Person_IMPL Person_IVARS;
    int _num;
    int _score;
};

那Person和Student对象占据空间是多大?实际需要考虑以下两个因素:

void theSizeOfPerson() {
    Person *person = [[Person alloc] init];
    person->_age = 16;
    
    // Person的成员变量累加占据的空间是12,因为结构体字节对齐,实际是16个字节
    NSLog(@"Person class_getInstanceSize: %zd", class_getInstanceSize([Person class]));
    
    // 实例对象占据的空间大小,16个字节
    NSLog(@"Person alloc size: %zd", malloc_size((__bridge const void *)person));
}

void theSizeOfStudent() {
    Student *stu = [[Student alloc] init];
    stu->_num = 4;
    
    // Student的成员变量累加占据的空间是20,因为结构体字节对齐,实际是24
    NSLog(@"Student class_getInstanceSize: %zd", class_getInstanceSize([Student class]));
    
    // 根据libmalloc源码,分配给对象的空间,是16的倍数,32个字节
    NSLog(@"Student alloc size: %zd", malloc_size((__bridge const void *)stu));
}

OC对象和结构体可以进行转化。

void instanceToStruct() {
    Person *person = [[Person alloc] init];
    person->_age = 4;

    struct Person_IMPL *personImpl = (__bridge struct Person_IMPL *)person;
    NSLog(@"person instance age is %d,struct personImpl age is %d", person->_age, personImpl->_age);
}

参考

苹果源码列表:https://opensource.apple.com/tarballs/
Demo地址:https://github.com/xiaoLong1010/DeepObjectiveC/tree/master/01-ObjectSize

上一篇 下一篇

猜你喜欢

热点阅读