Objective - C 底层

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

2020-04-22  本文已影响0人  爱玩游戏的iOS菜鸟

我们现在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方法,方法调用顺序总结:

  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底层源码到底做了什么事?
调用顺序:

问题3:为什么会实际使用40个字节?

·

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

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

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

上一篇下一篇

猜你喜欢

热点阅读