前言
最近看源码啥的看的头大,最近学一些容易理解其他知识
内存布局
iOS的内存分区分为五大分区:
栈区、堆区、全局区、常量区、代码区
栈区
创建临时变量时由编译器自动分配,在不需要的时候自动消除变量的存储区
主要用于存储函数的局部变量、函数参数和返回地址,遵循先进后出原则
优点:
- 栈是系统数据结构,每个线程独有一个栈,每个进程也独有一个栈,栈包含了线程执行所需要的所有局部变量,函数参数,返回地址等
- 快速高效,缺点是有限制,数据不灵活
- 同时栈的空间还分为静态分配与动态分配,静态分配是由编译器完成的,动态分配是运行时决定的
void function() {
int localVar = 10; // 局部变量,静态分配在栈上
char buffer[100]; // 在编译时分配固定大小的数组
}
void dynamicAllocation(size_t size) {
int runtimeArray[size]; // 变长数组,根据运行时的size决定大小
}
- 同时还存储了函数的隐藏参数
(id self,SEL _cmd)
传入函数的参数值、函数体内声明的局部变量等,由编译器自动分配释放,通常在函数执行结束后就释放了。(注意:不包括static
修饰的变量,static
意味该变量存放在全局/静态区)
堆区
特点:
- 堆是一块不连续的区域,类似于链表结构,便于增删不便于查询,遵循先进先出(
FIFO
)原则 - 堆区的内存一般是运行时分配的
存储内容:
- 堆区的内存是由程序员手动分配的,在ARC中经常会自动回收
- 在OC中经常使用
alloc
或是new
开辟堆空间和创建对象,在C语言中使用的malloc
等分配的空间需要手动释放
我们来看一个分配堆内存的例子
NSObject *obj = [NSObject new];
对于这段代码来说总共进行了四步
- 在堆内存中申请一块大小合适的空间
- 在这块内存空间里创建我们的对象
- 初始化对象的属性,为对象的属性赋默认值,==基本数据类型赋值0->C指针类型为NULL->OC指针类型为nil
- 返回这个对象在堆空间的地址,将这个地址赋给obj,以后我们访问obj变量时实际访问的就是堆空间中的的NSObject对象,但是obj变量因为本质上是一个指针变量,他是存储在栈空间的,这个我们后面验证
优点
灵活方便,数据适应面广泛
缺点
需手动管理,速度慢、容易产生内存碎片
全局区(静态区)
全局区分为两个区域,bss段与data段,用于存储全局变量与静态变亮
- BSS
用于存放程序中未初始化的或者初始值为0的全局变量的一块内存区域 - data
存放明确赋初值的全局和静态变量
常量区
特点:用于存储程序中的常量数据,如字符串字面量。
用途:这部分内存通常是只读的,确保常量值不会被程序意外修改。
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSString *string1 = @"Hello, World!";
NSString *string2 = @"Hello, World!";
if (string1 == string2) {
NSLog(@"string1 and string2 are the same instance");
} else {
NSLog(@"string1 and string2 are different instances");
}
// 创建不可变的 NSNumber 常量
NSNumber *number = @42;
NSLog(@"Number: %@", number);
}
return 0;
}
在这个例子中:
1. @"Hello, World!
" 是一个字符串字面量,它被存储在常量区。由于常量区中的字符串是唯一的,所以 string1 和 string2 实际上指向同一个内存地址。
2. @42
是一个 NSNumber
的常量,它也被存储在常量区。
代码区
程序代码区:用来存放函数的二进制代码
代码区需要防止运行时被非法修改,所以只允许读取不允许写入
总结
- 栈区存储临时变量、函数参数以及函数地址,数据结构符合先进后出
- 堆区存储运行时创建的对象,数据结构符合先进先出
- 常量区存储字符串字面量,常量区的字符串是唯一的
- 代码区存储存放函数的二进制代码,只允许读取操作
- 全局静态区存储全局变量与静态变量,分为两个区域,一个为BSS区存储未初始化的变量,另一区域存储已经初始化的全局静态变量