美文网首页
OC实例对象占据空间大小

OC实例对象占据空间大小

作者: 川少叶 | 来源:发表于2018-10-27 20:46 被阅读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的实现。

  • class_getInstanceSize实质是实例变量占据的空间大小,并且会进行字节对齐
  • allocWithZone中使用instanceSize分配空间大小,空间大小至少是实例变量占据的空间,如果小于16字节,则还是会分配16字节
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对象占据空间是多大?实际需要考虑以下两个因素:

  • 结构体中成员变量的字节对齐
  • 苹果分配空间的原则,字节数是16的倍数,见libmalloc源码
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

相关文章

网友评论

      本文标题:OC实例对象占据空间大小

      本文链接:https://www.haomeiwen.com/subject/eosozftx.html