内存地址分配
- 堆: 由程序员管理,内存分配使用的是 alloc;堆区的地址是从低到高分配的,是不连续的内存区域,从而堆获取的空间比较灵活。
- 栈: 由编译器自动分配,存放局部变量,函数的参数值等;后进先出,栈区的地址是从高到低,是一块连续的内存区域。即栈顶的地址和栈的最大容量是系统预先规定好的
- 全局、静态区: 包括两个部分,未初始化、初始化过;程序结束后由系统释放
- 常量区: 存放常量字符创,程序结束后由系统释放
- 代码区: 存放函数体的二进制代码
碎片问题
- 对于堆来讲,频繁地 new/delete 会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低
- 对于栈来讲,则不会出现该问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出
分配方式
- 堆都是动态分配的,没有静态分配的堆
- 栈有两种分配方式: 静态分配、动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由 alloc 函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放的,无需我们手工实现
分配效率
- 栈是机器系统提供的数据结构,计算机会在底层对栈提供支持
- 分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是 C/C++函数库提供的,机制相对比较复杂
注意事项
- 当 App 启动时,代码区、常量区、全局区大小已固定;因此指向这些区的指针不会产生崩溃性的错误,而堆区和栈区是时时刻刻变化的(堆的创建销毁、栈的弹入弹出),若指针指向的内存被释放,将造成野指针异常
- OC 中根据变量的引用计数是否为 0,系统识别是否将其回收
- 系统使用一个链表来维护所有已分配的内存空间
强引用 & 弱引用
- 强引用: 当前对象被其他对象引用时,会执行 retain 操作,引用计数 +1。当 retainCount = 0 时,该对象才会被销毁。因为我们要进行对象的内存管理,所以这是默认的引用方式(默认是强引用)
- 弱引用: 当前对象的生命周期不被其他对象引用限制,它本该什么时候销毁就什么时候销毁。即使它的引用没断,但是当它的生命周期到了就会被销毁
属性的内存管理
- alloc、new: 类初始化方法,开辟新的内存空间,引用计数 + 1
- retain: 不会开辟新的内存空间,引用计数 +1
- copy: 把一个对象复制到新的内存空间,新的内存空间引用计数 + 1,旧的不会;其中分为浅拷贝和深拷贝,浅拷贝只是拷贝地址,不会开辟新的内存空间;深拷贝是拷贝内容,会开辟新的内存空间
- assign: 一般用来修饰基本数据类型,也可作用于对象类型,但是当所指向的对象销毁时不会将当前指向对象的指针置 nil,会有野指针生成
- strong: 用来修饰 Ojc 对象,引用计数 + 1
- release: 释放对象,引用计数 -1
- autorelease: 延迟释放, 自动释放池,当执行之后引用计数 -1;
- weak: ARC 下引入修饰词,可代替 assign,比 assign 多增加一个特性(置 nil),防止野指针异常,weak 只能作用于对象类型,不能用于基本数据类型
Q: IB 连接的对象为什么可以使用 weak?
~~ VC 对象不是直接拥有 IBOutlet 对象,而是间接拥有它,因此只要 view 不被释放,view 上的 IBOutlet 对象的引用计数都不会为 0
- unsafe_unretained: 指针指向的对象一旦被释放,这些指针就会变成野指针
内存的管理机制
- 当一块内存被创建后,它的引用计数从 0 -> 1,表示有一个对象或指针持有这块内存。
- 当有另外一个对象或指针指向这块内存时,引用计数从 1 -> 2
- 当其中一个对象或指针不再指向这块内存,引用计数 -1
- 当这块内存的引用计数变为 0 时,表示没有任何对象或指针持有这块内存,系统便会立刻释放掉这块内存
野指针异常
对象内存空间已经被系统回收,仍然使用指针操作这块内存
内存管理方式
- ARC(Automatic Reference Count): iOS 5 推出的功能,本质还是 MRC,编译性特性,是一种代码静态分析的工具,让编译器在编译代码的时候,自动生成实例的引用计数管理部分代码,简单来说就是自动加入 retain / release
- MAC(Manual Reference Count): 人工引用计数,内存的开辟和释放都由程序员代码控制,相对于垃圾回收来说,对内存的控制更加灵活,可以在需要释放的时候及时释放
- 垃圾回收(GC): 程序员只需要开辟内存空间,由系统来判断哪些空间不再被使用,并回收内存空间,以便再次分配(JAVA 开发中使用)
// TODO
网友评论