006-内存管理

2017-11-26  本文已影响9人  Yasic

内存管理

对于面向对象语言,离不开对象的声明和操作,而本质上来说对象就是在内存里开辟的一段空间,用来放置数据和方法。对于这段内存空间的声明周期需要进行管理,objectivec 中大致有三种机制来管理内存空间。

其中垃圾回收机制在 iOS 中是不支持的,而 iOS 5 所引入的 ARC 是目前最推荐的内存管理机制,MRC 则是早期 objectivec 所采取的方式。

一、对象的生命周期

因为要管理对象的内存,因此有必要知道 objectivec 中的一个对象从创建到销毁具体经历了哪些过程。

1. 创建

完整流程

在对象的创建过程中,系统在栈区开辟空间存储当前对象的指针变量,在堆区中开辟空间存储对象数据,并将栈区的指针指向堆区中的内存空间。objectivec 采用两段式构造模式,这一点在《对象与使用》中已经提到过

Animal * animal = [[Animal alloc]init];

而具体来说这里其实调用了 alloc 和 init 两个方法。

alloc 方法

该方法是一个类方法,将会在堆分区中为需要创建的对象开辟合适的内存空间,同时返回一个未被 “初始化” 的对象,具体来说做了以下事情。

所以 alloc 返回的对象的成员变量还没有初始化,因此需要调用 init 方法。

init 方法

init 方法对 alloc 返回的对象进行真正的初始化工作。

在某些情况下,init 会造成 alloc 的原本空间不够用,从而二次分配空间,因此 alloc 和 init 需要放在一行调用。另外,基于类簇的概念,init 方法会根据接收到的参数决定返回什么样的对象更合适,所以不应分开使用这两个方法。

new 方法

new 可以简单理解为 alloc 与 init 方法的联合调用。

2. 销毁

如前面所述,当对象的引用计数为 0 时将会被系统销毁,具体来说会调用该对象的析构函数—— dealloc 方法。在 dealloc 方法中会释放对象所分配的资源及其成员变量关联的内存。一旦重写 dealloc 方法就必须在代码块最后调用 [super dealloc] 方法,调用 [super dealloc] 方法是为了让 super 释放相应的资源。同时不要自己调用 dealloc 方法。

二、引用计数

如何判断一个对象是否需要被回收有很多策略,objectivec 采用的是引用计数机制,为每一个对象绑定一个NSUInteger 类型的整数来表示该对象的引用次数,即当前有多少引用者正在引用此对象。如果把引用计数理解为一种拥有权,那么引用计数机制是在维护一个对象目前正在被多少指针变量拥有。

这样,如果一个对象的引用次数不为0,则表示当前存在指针变量正在拥有这一对象,所以不能回收。一旦对象引用次数为0,则表示该对象可以被销毁回收了。

引用计数机制的作用在于它解决了从其他方法获得的对象的内存管理权的转移问题。当对象 A 将一个对象 N 发送给对象 B 以后,B 如果需要长时间使用则通过增加对象 M 的引用计数获得拥有权,这样对象 A 可以安全地放弃对象 M 的引用不用担心因此而使 M 被回收从而 B 在使用时发生异常。

相关操作

对于对象引用计数有改变的操作主要有

对象 方法 引用计数变化
生成并持有对象 alloc/init/new/copy/mutableCopy等 +1
持有对象 retain +1
释放对象 release -1
废弃对象 dealloc 在调用release时间接调用

管理原则

自动释放 autorelease

release 会立即释放当前对象的引用,而 autorelease 是一种预约式的释放,会在一次 runloop 结束后释放引用。之所以要使用自动释放是因为很多时候我们不想关心或者不能确定当前对象的具体释放时间,使用自动释放可以延迟对象的释放时机。

Cat * cat = [[[Cat alloc] init] autorelease];

自动释放实际上是将该对象放入当前 Autorelease Pool 中,在当前 Autorelease Pool 释放时,会对添加进该 Autorelease Pool 中的对象逐一调用 release 方法。

自动释放在返回对象时有用,

+(Animal *)getAnimal
{
    Animal * animal = [[Animal alloc] init];
    return animal;
}

由于 alloc 和 init 都会将引用次数加一,因此返回的 animal 的引用次数比预期多1,但是调用者不知道这一细节,所以需要方法内进行释放。但调用 release 释放是立刻执行的,因此将导致无法返回有效的对象,这时就需要 autorelease 将其放入自动释放池中,能保证调用者获得一个可以使用的对象。

这里要注意

自动释放池有两种实现方式

三、ARC 自动引用计数机制

垃圾回收机制是在 objectivec 中引入的特性,类似于 Java 中的自动垃圾回收,但是在 iOS 中从未支持过,因此不考虑。MRC 手动内存管理就是指通过上面提到的 retain、release 等方式进行手动的内存管理,但是会产生“野指针”和“内存泄漏”等问题。

ARC作为WWDC2011和iOS5之后LLVM 3.0编译器的一项新特性,其本质是由编译器在编译阶段在程序合适位置加入引用计数操作代码,所以为了保证这一机制能够正常运行,在 ARC 模式下 xcode 不允许在代码中加入类似 retain、release 等操作 ,也禁止复写内存管理方法。

具体来说,ARC 会操作的地方有

内存管理与属性特性

修饰属性的部分属性特性在内存管理里有很大关联。

指针类型

与属性特性类似,ARC 的指针也有内存管理的特性

上一篇下一篇

猜你喜欢

热点阅读