美文网首页Objective - C 底层
Objective - C 对象的本质(二) 实例对象的内存

Objective - C 对象的本质(二) 实例对象的内存

作者: 爱玩游戏的iOS菜鸟 | 来源:发表于2020-04-22 16:51 被阅读0次

    我们现在main.m文件中定义Student对象,如下:

    #import <objc/runtime.h>
    #import <malloc/malloc.h>
    
    
    struct Student_IMPL {
        Class isa;
        int _no;
        int _age;
    };
    
    @interface Student : NSObject{
        @public
        int _age;
        int _no;
    }
    
    @end
    
    @implementation Student
    
    
    @end
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            Student *stu = [[Student alloc]init];
            stu->_no = 4;
            stu->_age = 8;
            
            NSLog(@"%zd",class_getInstanceSize([Student class]));//16
            NSLog(@"%zd",malloc_size((__bridge void *)stu));//16
            
            struct Student_IMPL *stuImpl = (__bridge struct Student_IMPL *)stu;
            NSLog(@"%d, %d",stuImpl->_no,stuImpl->_age);//4, 8
            
        }
        return 0;
    }
    

    (一)OC对象的内存分配

    和上一章一样,转换为C++代码,过程不再赘述,我们找到Student类的定义:

    struct Student_IMPL {
        struct NSObject_IMPL NSObject_IVARS;
        int _age;
        int _no;
    };
    
    
    // @property(nonatomic,assign) NSInteger age;
    // @property(nonatomic,assign) NSInteger no;
    
    /* @end */
    

    我们可以看出,Student结构体包含了NSObject结构体,也就是Student的前8个字节是NSObject的成员变量,后面4+4个字节可能是age、no两个成员变量的内存空间(int类型占4个字节)`

    我们使用Student_IMPL结构体指针访问stu的成员,成功!

    struct Student_IMPL {
        Class isa;
        int _no;
        int _age;
    };
    
    struct Student_IMPL *stuImpl = (__bridge struct Student_IMPL *)stu;
    NSLog(@"%d, %d",stuImpl->_no,stuImpl->_age);//4 8
    

    通过lldb也证实上面的结论:


    lldb证实 各占用16个字节,具体论证方法不再赘述(1.c++代码 2. lldb 3. 两个函数)

    分析:
    Person对象至少需要12(8+4)个字节,最后分配16个字节,空了4个字节,即使没有至少16字节的要求,根据内存对齐,Person对象也是16个字节
    Student继承自Person,至少需要16个字节,没有空闲字节

    (1)内存分配注意点

    我们再看下面的Student类,一个student对象占用多少个字节?:

    @interface Student : NSObject{
        @public
        int _age;
        NSInteger _number;
        int _no;
        NSString *_str;
    }
    

    答:至少占用40个字节 isa(8)+age(8 浪费4个)+number(8)+no(8 浪费4字节)+str(8) = 40 因为16个字节为一捆,所以实际分配48个字节;

    为什么会产生这样的效果呢?

    问题1:为什么会分配48个字节?

    我们还是从对象的初始化开始看,类调用alloc方法,底层实际上调用了allocWithZone方法,方法调用顺序总结:

    • allocWithZone
    • _objc_rootAllocWithZone
    • _class_createInstanceFromZone方法
      _class_createInstanceFromZone方法中,我们找到下面的代码:
      size = cls->instanceSize(extraBytes);//获取对象至少需要(内存对齐后的)多少字节数   40
      if (outAllocatedSize) *outAllocatedSize = size;
    
      id obj;
      if (zone) {
        obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
      } else {
        obj = (id)calloc(1, size);//重点 size为40 为什么最后分配了48个字节呢?
      }
    

    我们查看calloc底层源码到底做了什么事?
    调用顺序:

    • calloc
    • malloc_zone_calloc
      在系统底层,分配的字节数永远是16个字节的倍数,因此会分配48个字节
    问题3:为什么会实际使用40个字节?

    ·

    猜想: 如果number变量与no变量交换位置,会产生什么效果?
    答:age与no连续则至少占用32个字节,实际分配32个字节

    如果因为int只占用4个字节,如果两个int变量连续,则刚好不会有内存浪费;不同的成员占用长短字节数不一则会产生内存浪费

    所以在定义类时,如果存在占用字节数不同的成员变量,尽可能按字节占用顺序,节约内存空间

    相关文章

      网友评论

        本文标题:Objective - C 对象的本质(二) 实例对象的内存

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