【iOS】—— 内存的五大分区

内存的五大分区

五大分区分别是

栈区(系统管理的地方)、堆区(程序员控制的地方)、静态区(全局区)、常量区、代码区。这里的内存指的就是RAM(random access memory)。

在这里插入图片描述

1.栈区

栈是由编译器自动分配释放来管理内存。用户存放程序临时创建的变量、存放函数的参数值、局部变量等。由于栈的先进后出特点,所以特别适合用来做保存/恢复现场的操作。从这个吧意义上,我们可以把栈看做一个临时寄存、交换的内存区。

栈区(stack): 是一段连续的内存区域,从高地址向低地址存储,遵循先进后出(FILO)原则。 一般在运行时进行分配,内存空间由系统管理,变量过了作用域范围后内存便会自动释放。参数、函数、局部变量都放在栈区。参数入栈是从前往后入栈;而结构体入栈是从后往前入栈

优点:

  • 栈是系统数据结构,对应线程/进程是唯一的。
  • 快速高效,缺点是有限制,数据不灵活。[先进后出]
  • 栈空间分静态分配和动态分配两种。
  • 静态分配是编译器完成的,比如自动变量(auto)的分配。
  • 动态分配由alloc函数完成。
  • 栈的动态分配无需释放(是自动的),也就没有释放函数。
  • 为可移植的程序起见,栈的动态分配操作是不被鼓励的!

在之前学习小蓝书第一章中提到过实例和对象的内存,我们以下面的例子来具体看一下吧:

NSObject *obj = [NSObject new];

对于这一行代码来说,obj是1个指针变量,不是1个对象,存储在栈区,真正的创建对象,new做的事情有四步:

1.在堆内存中申请一块合适大小的空间
2.在申请的这块空间中根据类的模板创建对象
3.初始化对象的属性,为对象的属性赋默认值
->如果属性的类型是基本数据类型,就赋值为0
->C指针类型 NULL
->OC指针类型 nil
4.返回这个对象在堆空间的地址
将这个地址赋值给obj指针
obj指针指向了堆空间中的NSObject对象

2.堆区

堆区(heap): 由程序员分配和释放,如果程序员不释放,程序结束时,可能会由操作系统回收 ,比如变量通过new、alloc、malloc、realloc分配的内存块就存放在堆区。

堆向高地址扩展的数据结构,是不连续的内存区域。 程序员负责在何时释放内存,在ARC程序中,计数器为0的时候,在当次的runloop结束后,释放掉内存。堆中的所有东西都是匿名的,这样不能按名字访问,而只能通过指针访问。
对于堆来讲,频繁的new/delete势必会造成内存空间的不连续性,从而造成大量的碎片 ,使程序效率降低。

优点:

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

3.常量区

文字常量区: 存放常量字符串,程序结束后由系统释放

该区是编译时分配的内存空间,在程序运行过程中,此内存中的数据一直存在,程序结束后由系统释放。
存放常量:整型、字符型、浮点、字符串等。

4.静态区

全局区(静态区) (static): 全局变量和静态变量的存储是放在一起的,初始化的全局变量和静态变量存放在一块区域,未初始化的全局变量和静态变量在相邻的另一块区域,程序结束后由系统释放。

注意:全局区又可分为未初始化全局区(BSS段)和初始化全局区(DATA段)。

举例:

int a;未初始化的;.bss
int a = 10;已初始化的。.data

5.代码区

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

static、extern、const关键字比较

1.static关键字

static NSString *name;

static关键字作用:

  • 1.可以修饰局部变量,将局部变量存储到静态存储区。
  • 2.可以修饰全局变量,限定全局变量只能在当前源文件中访问。
  • 3.可以修饰函数,限定该函数只能在当前源文件调用。

全局静态变量

优点:不管对象方法还是类方法都可以访问和修改全局静态变量,并且外部类无法调用静态变量,定义后只会指向固定的指针地址,供所有对象使用,节省空间。
缺点:存在的生命周期长,从定义直到程序结束。
建议:从内存优化和程序编译的角度来说,尽量少用全局静态变量,因为存在的生命周期长,一直占用空间。程序运行时会单独加载一次全局静态变量,过多的全局静态变量会造成程序启动慢。

局部静态变量

优点:定义后只会存在一份值,每次调用都是使用的同一个对象内存地址的值,并没有重新创建,节省空间,只能在该局部代码块中使用。
缺点:存在的生命周期长,从定义直到程序结束,只能在该局部代码块中使用。
建议:局部和全局静态变量从本根意义上没有什么区别,只是作用域不同而已。如果值仅一个类中的对象和类方法使用并且值可变,可以定义全局静态变量,如果是多个类使用并可变,建议值定义在model作为成员变量使用。如果是不可变值,建议使用宏定义。

2.extern全局变量

这个在小蓝书第一章也有提及,分为两种:

//.m中要定义
NSString *name;

//.h中同时要定义
extern NSString *name;

对内的全局变量

优点:不管对象方法还是类方法都可以访问和修改全局静态变量,并且外部类无法调用静态变量,定义后只会存一份值,供所有对象使用,节省空间。跟全局静态变量一样,只是少了static修饰少了static特性。
缺点:存在的生命周期长,从定义直到程序结束。
建议:都跟全局静态变量都一样了,还需要用对内的全局变量吗?不用extern修饰就少了extern的特性,还不如用全局静态变量,至少能明确的知道static是对内使用的。

外部全局变量

优点:除了该类,其他文件也可以访问该变量。
缺点:存在的生命周期长,从定义直到程序结束。并且外部可以修改其值,出现错误不容易定位。
建议:使用全局变量的原因就在于其对外的特性,但是其使用的方便性没有使用model的属性或宏来得方便。程序启动的时候会单独加载全局变量,同理与全局静态变量,少使用。

== 全局静态变量与全局变量 其实本质上是没有区别的,只是存在修饰区别,一个static让其只能内部使用,一个extern让其可以外部使用。 ==

3.const常量

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

猜你喜欢

转载自blog.csdn.net/m0_62386635/article/details/128726532
今日推荐