【iOS】内存分区

五大分区是什么?

在编写代码变量都是怎么在内存中存取的呢?其实内存中分为五大分区,分别是栈区(系统管理的地方)、堆区(程序员控制的地方)、常量区(全局区)、静态区、代码区。这里我们还要知道内存指的就是RAM

在这里插入图片描述

栈区

创建临时变量时由编译器自动分配,在不需要的时候自动消除变量的存储区。
主要用于存储函数的局部变量、函数参数和返回地址,遵循一个先进先出的原则我们可以把栈看做一个临时寄存,交换的内存区

用户栈在程序执行期间可以动态地扩展和收缩

优点:

  • 栈是系统数据结构,对应的线程/进程是唯一的,栈中存储了进程所需要的所有局部变量、函数的参数等内容
  • 栈的优点是快速高效,但是缺点是有限制,数据不灵活
  • 栈空间分为静态分配和动态分配两种,静态分配由编译器完成,动态分配在运行时决定的。
  • 为可移植的程序起见,栈的动态分配操作是不被鼓励的。

堆区

就是由allocnewmallocrealloc创建的对象所分配的内存块,他们的释放系统不会主动去管,需要我们开发者去告诉系统什么时候释放这块内存;若是程序猿不释放,程序结束后系统会自己释放。

堆是向高地址扩展的数据结构,是不连续的内存区域,程序员负责在何时释放内存(在ARC中通常会自动回收)。

优点:

  • 灵活方便、数据适应面广泛,但是效率有一定降低
  • 堆是函数库内部数据结构,不一定唯一
  • 不同堆分配的内存无法相互操作
  • 堆空间的分配总是动态的
  • 虽然程序结束时所有的数据结构都会被释放回系统,但是精准的申请内存,释放内存匹配时良好的程序基本要素

这里我们以下面这个分配堆内存为例:

NSObject* obj = [NSObject new]
  1. 在堆内存中申请一块大小合适的空间
  2. 在这块内存空间里创建我们的对象
  3. 初始化对象的属性,为对象的属性赋上默认值,基本数据类型赋值0 —— C语言类型为NULL —— OC指针类型为nil
  4. 返回这个对象在堆空间的地址,将这个地址赋给obj,这样以后我们访问obj变量时实际访问的就是堆空间中的NSObject对象,但是obj变量因为本质上是一个指针变量,他是存储在栈空间的

在这里插入图片描述

静态区 全局区

全局区分为两个区域,用于存储全局变量以及静态变量

  • .bss:存放未初始化的全局变量、静态变量
  • .data:已初始化的全局变量、静态变量

常量区

存放常量字符串,程序结束后由系统释放,该区是编译时分配的内存空间,在程序运行过程中,这个内存中的数据一直存在,程序结束后由系统释放。

代码区

用来存放函数的二进制代码,代码段需要防止在运行时被非法修改,故而只允许读取操作,不允许写入操作

static、extern、const比较

static

  • 全局静态变量

优点:无论对象方法还是类方法都可以访问和修改全局静态变量,并且外部类无法调用静态变量,定义后只会指向固定的指针地址,供所有对象使用,节省空间。

缺点:存在的生命周期长,从定义直到程序结束

建议:从内存优化和程序编译的角度来说,尽量少用全局静态变量,因为存在的生命周期长,一直占用空间。程序运行时会单独加载一次全局静态变量,过多的全局静态变量会造成程序启动慢。

static int counter = 0;
//可以在一个类中多个方法中使用
void increment() { counter++; }
void print_counter() { printf("Counter: %d\n", counter); }
  • 局部静态变量

优点:定义后只会存在一份值,每次调用都是使用的同一个对象内存地址的值,并没有重新创建,节省空间,只能在该局部代码块中使用。

缺点:存在的生命周期长,从定义直到程序结束,只能在该局部代码块中使用。

建议:局部和全局静态变量从本根意义上没有什么区别,只是作用域不同而已。如果值仅一个类中的对象和类方法使用并且值可变,可以定义全局静态变量,如果是多个类使用并可变,建议值定义在model作为成员变量使用。如果是不可变值,建议使用宏定义。

void log_message(const char* msg) {
    static int log_count = 0;
    printf("[%d] %s\n", ++log_count, msg);
}//仅在该函数中可以使用,不可以被外界访问

extern全局变量

//.m中要定义
NSString* name;
//.h中同时也要定义
extern NSString* name;
  • 对内的全局变量:没有用extern在.h中修饰的变量,仅定义在.m文件中让该变量只能在该类使用

优点:不管对象方法还是类方法都可以访问和修改全局静态变量,并且外部类无法调用静态变量,定义后只会存一份值,供所有对象使用,节省空间。跟全局静态变量一样,只是少了static修饰少了static特性。

缺点:存在的生命周期长,从定义直到程序结束。

建议:都跟全局静态变量都一样了,还需要用对内的全局变量吗?不用extern修饰就少了extern的特性,还不如用全局静态变量,至少能明确的知道static是对内使用的。

  • 对外的全局变量:除了该类,其他文件也可以访问该变量

优点:除了该类,其他文件也可以访问该变量

缺点:存在的生命周期长,从定义到程序结束,并且外部可以修改其值,出现错误不容易定位。

建议:使用全局变量的原因在于其对外的特性,但是其使用的方便性没有使用model的属性或宏来得方便。程序启动的时候会单独加载全局变量,同理与全局静态变量,少使用。

const常量

不同于变量,常量的值是固定不可变的,一般用于只读值

  • 优点:只可以读取值,不能修改。一般用于接口或者文字显示这种固定值。添加extern可以对外全局常量,任意位置都可以访问。
  • 缺点:存在的生命周期长,从定义直到程序结束。需要在.h .m中分别定义代码较多。
  • 建议:良好的编码习惯而言,少使用宏,多使用常量。因为常量声明是有明确类型的,而宏只是替换并不能进行类型判断。不够严谨。
//.h中定义extern
extern NSString *const name;
//.m中定义值
NSString *const name = @"123";

//const声明部位不同,意义也不同。const定义的是其右边整体不可变。


//*const name1定义的是 name1 不可变。 name1是指针。
//因此 不能通过修改name1而指向其他值。常规的const使用这个方法定义不可修改的值
NSString *const name1 = @"456";

//const * name定义的是 * name不可变, 而*name指向的是@"789",
//也就是说@"789"这个内存地址的值不可变为其他值。
NSString const * name2 = @"789";
//但name指针可以指向其他值,所以该定义方式无法保证值的唯一性。
NSString *newName = @"222";
name2 = newName;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值