说到iOS的内存管理,大家首先想到的可能是引用计数相关的东西,而跟引用计数相关的内存都是分布在堆区(heap),也就是说我们平时关注最多的部分都是堆区的内存。其实在iOS系统中(其他操作系统也一样),内存的分布区域大致可以分为三个部分:栈区(stack),堆区(heap),和全局静态区(global)。其中栈区主要存放局部变量和函数参数等相关变量,超出作用域之后自动释放,堆区存放alloc,new等关键字生成的对象;全局静态区主要存放静态数据,全局数据和常量,程序运行之后一直存在。分布如下图所示:
在说内存分布之前,先来理一理几个概念:静态数据,全局数据和常量。定义在类外面的都是全局数据,前面加const的,就是常量数据,加static的是静态数据。
通过字符串的初始化,来分析内存的分布
NSString* str1 = @"123";
NSLog(@"str1 = %p, str1 = %@",str1,str1);
NSString* str2 = @"123";
NSLog(@"str2 = %p, str2 = %@",str2,str2);
NSString* str3 = [[NSString alloc]initWithFormat:@"123"];
NSLog(@"str3 = %p, str3 = %@",str3,str3);
NSString* str4 = [[NSString alloc]initWithFormat:@"123"];
NSLog(@"str4 = %p, str4 = %@",str4,str4);
NSString* str5 = [[NSString alloc]initWithString:str3];
NSLog(@"str5 = %p, str5 = %@",str5,str5);
NSString* str6 = [[NSString alloc]initWithString:str3];
NSLog(@"str6 = %p,str6 = %@",str6,str6);
打印结果如下:
首先@"123"是字符串常量,被分配在内存的常量区; str2也指向了这个常量,所以str1和str2的对应的指针是一样的。str3指向了一个alloc出来的对象,这个对象是被分配在堆内存上的,所以str3和str2对应的指针不一样了。可能大家会感到奇怪,str4也指向了一个alloc出来的对象,这个对象也是被分配在堆内存上的,为什么会跟str3的指向相同呢?其实这是iOS系统对字符串内存的优化:如果一个字符串是通过一个字符串常量初始化而来的,那么这个字符串里面的值可以直接从常量区去拿,这样就不需要为每一个在堆上面的字符串分配一块儿新的内存。str5和str6都是指向堆上面的内存,并且是通过变量来初始化的,所以对应的指针不一样。
上面一大段文字说的都是指针对应的指向,而str1--str6本身是一个指针,又都是在函数中定义的,是局部变量,所以被分配到栈区。综上可以得出如下的内存分布图: