ios底层探索

iOS 内存对齐

2020-11-24  本文已影响0人  Jeffery_zc
数据类型所占字节.png

1. 对象内存对齐

在我们进行alloc一个对象的时候,通过源码探究可以得出。一个对象进行内存的开辟,是以16字节进行对齐的。但是,对象真正需要的内存,却是以8字节对齐的。只是系统为了内存安全考虑,采取16字节对齐的方式开辟内存空间。

ZCPerson.h

@interface ZCPerson : NSObject

@property (nonatomic, copy) NSString *name;

@property (nonatomic, copy) NSString *nickname;

@property (nonatomic, assign) int age;

@property (nonatomic, assign) double height;

@end

main.m

int main(int argc, char * argv[]) {
    @autoreleasepool {
       
        ZCPerson *p = [[ZCPerson alloc]init];
        p.name      = @"zc";
        p.age       = 18;
        p.height    = 185.0;
           
        NSLog(@"%@---%lu---%lu---%lu",p,sizeof(p),class_getInstanceSize([ZCPerson class]),malloc_size((__bridge const void *)(p)));
        
    }
    return 0;
}

输出:

2020-11-24 10:36:39.442212+0800 alloc[2456:59130] <ZCPerson: 0x600002156bb0>---8---40---48

通过打印,我们可以看到第一个是输出ZCPerson对象,第二个打印的是p指针,指针在内存中占据8个字节。第三个打印是ZCPerson所需内存,我们看到输出结果是40,如果是按照alloc源码分析,以16字节对齐的话,应该和最后输出的一样是48,所以从输出结果看,ZCPerson所需内存是不可能以16自己对齐的。

size_t class_getInstanceSize(Class cls)
{
    if (!cls) return 0;
    return cls->alignedInstanceSize();
}
 uint32_t alignedInstanceSize() const {
        return word_align(unalignedInstanceSize());
    }
#   define WORD_MASK 7UL

static inline uint32_t word_align(uint32_t x) {
    return (x + WORD_MASK) & ~WORD_MASK;
}

通过观察class_getInstanceSize方法源码发现,ZCPerson实际所需要的内存空间是以8字节对齐进行开辟的。

@property (nonatomic, copy) NSString *name;

@property (nonatomic, copy) NSString *nickname;

@property (nonatomic, assign) int age;

@property (nonatomic, assign) double height;

@property (nonatomic) char c1;

@property (nonatomic) char c2;
内存优化.png
通过x命令可以打出地址内存情况,通过x/4gx可以将内存情况以16进制打印出4组数据。

输出ZCPerson:

(lldb) po p
<ZCPerson: 0x600003f11260>

打印ZCPerson地址内存情况:

x/4gx 0x600003f11260
0x600003f11260: 0x000000010b644858 0x0000001200006261
0x600003f11270: 0x000000010b642068 0x000000010b642088
(lldb) po 0x000000010b644858
ZCPerson

(lldb) po 0x000000010b642068
zc

(lldb) po 0x000000010b642088
handsomeboy

从以上输出可以看到,这里左边打印出的是地址,右边对应的是地址里存放的是属性的值。但是,当我们打印第一行第二列的值的时候,发现打出来的是乱码:

(lldb) po 0x0000001200006261
77309436513

这是因为,ZCPerson内存被优化的结果。在上述中我们可以看到,给ZCPerson属性赋值的还有age,c1,c2,而这些值,正好被存储在0x0000001200006261这里。
验证如下:

(lldb) po 0x00000012
18

(lldb) po 0x00000000000061
97

(lldb) po 0x00000000000062
98

通过打印我们可以看到打印出了18,而打印出的97,98也正好对应了ASCII码中的a,b,从而,也说明了ZCPerson在开辟内存的时候,进行了内存的重排,这样做是为了避免内存的浪费,起到优化内存的作用。

2. 结构体内存对齐

在iOS中,类在底层会被编译成结构体,那么结构体是否是和类一样也是按照16字节对齐的呢?
我们定义2个结构体如下,然后打印出他们的内存大小:

struct ZCStruct1 {
    double a;   
    char b;    
    int c;    
    short d;   
}struct1;

struct ZCStruct2 {
    double a;  
    int b;      
    char c;     
    short d;   
}struct2;
NSLog(@"%lu -- %lu",sizeof(struct1),sizeof(struct2));

按照猜想,在struct1中,a的字节大小为8,b的字节大小为1,c的字节大小为4,d的字节大小为2,加起来等于15,按照16字节对齐原则,打印出来的的struct1的内存大小应该为16,同理,struct2打印出来的大小也应该为16。通过输出,我们发现打印结果与我们的猜想,完全不一致,哈哈,猜想错误。

打印结果:

2020-11-24 15:16:52.961015+0800 alloc[6174:186368] 24 -- 16

在结构体中进行内存对齐时,要遵循以下原则:

按照以上原则,我们重新来计算struct1的大小:

同理,我们可以得到struct2的大小为16。

struct1.png
struct2.png
上一篇 下一篇

猜你喜欢

热点阅读