iOS 中的内存管理

Posted Haley_Wong

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS 中的内存管理相关的知识,希望对你有一定的参考价值。

在讲述ios中的内存管理之前,先介绍一下其他语言中的内存管理,其实内存管理主要讲的是堆内存的管理,因为其他类型的内存,比如栈内存、全局变量/静态区内存、常量等各种语言的内存管理都差不多。

Java中的内存管理
Java中的内存管理由JVM完全负责,java中的"垃圾回收器(GC)"负责自动回收无用对象占据的内存资源。这里的GC回收的都是通过new关键字创建出来的被分配到堆内存中的对象。

大多编程语言,在函数中定义的一些基本类型的变量和对象的引用变量都是在栈内存中分配的。当在函数中定义一个变量时,java就会在栈中为这个变量分配内存空间,当超过变量的作用域后,java会自动释放掉为该变量分配的内存。

而通过new关键字创建的对象和数组等容器对象,会在堆中分配存储空间。在堆中产生了一个对象后,还可以在栈中定义一个特殊的变量,这个变量的取值等于对象在堆内存中的首地址,在栈中的这个特殊的变量就变成了对象的引用变量,以后可以在程序中使用栈内存的引用变量来访问堆中的对象,引用变量相当于为对象起的一个名字或者代号。

C中的内存管理
C中的内存则需要自己主动申请,和主动的释放。

同大多数编程语言一样,在C语言的函数中创建的自动变量、函数形参等都存储在栈中,当超出变出作用域(函数的右括号时,则自动被释放)。

而通过malloc函数主动申请的内存会被分配到堆上,当需要释放的时候,需要开发者主动调用free()函数来释放内存。所以不注意的话非常容易忘了释放而导致内存泄漏。

Objective-C中的内存管理
是在C语言的基础上实现的面相对象语言,表面上看与C语言很类似,通过一些特殊的关键字,进行对象变量的创建,也需要在合适的时机主动的调用realese函数来释放。

实际上,却与C语言的内存管理差别很大,Objective-C中是使用引用计数来做内存管理。
一个对象被创建出来后,被自己持有,此时引用计数为1,当其他变量持有该对象时,引用计数加1。
当持有者调用release 时,引用计数减1。当对象的引用计数为0时,内存就会被释放回收。

Objective-C中的内存管理函数

OC中有几个专门的函数,用于处理内存的管理。

1.对象的生成和持有

我们可以通过alloc/new/copy/mutableCopy等方法 生成对象,并获取对象的所有权。
这些函数执行后,就会生成一个新的对象,此时对象的引用计数为1。

2.持有对象

我们可以执行[obj retain],来持有对象,该操作会使对象的引用计数加1。
所以对对象执行多少次retain,引用计数就会加几。

3.释放对象

当我们执行[obj release]操作,就是释放对象,而该操作会使对象的引用计数减1。
而当对象的引用计数为0,这时候就会触发dealloc方法,系统就会回收对象所占的内存。系统通过dealloc方法告知开发者,该对象将要被销毁。

4.废弃对象(销毁对象)

是当对象的引用计数为0时,由系统会主动调用-(void)dealloc方法。所以,我们就可以在该函数内来释放掉其他一些系统不会自动释放或销毁的对象。
在苹果的开源代码objc4中,有一段宏中有关于release相关的实现。

而以上这些函数,基本都是对象的基类NSObject中的方法,而NSObject则属于Foundation框架,所以我们OSX、iOS中都可以使用这些函数来做内存的管理。

5.持有和释放原则

基于以上这些函数,有一些持有和释放的原则:

  • 1.自己生成并持有的对象,需要自己主动释放。
  • 2.非自己生成的对象,自己也可以执行retain函数来持有它。
  • 3.自己持有的对象不再需要时,必须主动释放。
  • 4.非自己持有的对象,禁止释放(这里非自己持有的对象,虽然可以调用release函数,但是必然会引起释放过度而导致访问错误的内存地址而引起Crash)。
// **** 情形1 *****
// 自己生成并持有对象
id obj = [[NSObject alloc] init];
// 自己主动释放
[obj release];

// ***** 情形2 和情形3 ******
// 取得非自己生成并持有的对象
id array = [NSMutableArray array];
// 持有该对象
[array retain];
// 释放对象
[array release];

6.补充说明

Objective-C中对一些函数的命名有特殊的含义。由于alloc/new/copy/mutableCopy这些方法会生成并持有对象,所以如果是以这些函数名开头,且满足驼峰命名规则的函数,一般也意味着自己生成并持有对象。比如:

  • allocMyObject
  • newThatObject
  • copyThis
  • mutableCopyYourObject

其中,copy方法一般是基于NSCopying协议实现,需要实现- (id)copyWithZone:(nullable NSZone *)zone;协议方法,方可调用[xxx copy]
mutableCopy是基于NSMutableCopying协议实现,需要实现- (id)mutableCopyWithZone:(nullable NSZone *)zone;协议方法,才可以调用[xxx mutableCopy]

另外,像allocMyObject这种自己生成并持有对象的函数一般是怎么实现的呢?

- (id)allocMyObject

    // 自己生成并持有对象
    id obj = [[NSObject alloc] init];
    // 将持有对象的变量直接返回,那么调用方就持有对象了。
    return obj;

这样,调用方就可以在不需要该对象时,主调调用release来释放对象了。

而一般的生成但不持有对象的函数怎么实现呢?

- (id)object

    // 自己生成并持有对象
    id obj = [[NSObject alloc] init];
    // 将对象加入自动释放池,获取到非自己持有的对象
    [obj autorelease];
    // 返回非自己持有的对象
    return obj;

这样外部就不用自己去释放对象了,另外加入自动释放池中的对象,会下一次runloop结束,进入睡眠前释放掉。

执行release会立刻释放释放对象。
执行autorelease不会立即释放对象,而是将对象主持到autoreleasepool中,取得注册后的对象,而不持有对象,当pool结束的时候,会使pool中的所有对象调用release方法。

以上是关于iOS 中的内存管理的主要内容,如果未能解决你的问题,请参考以下文章

iOS 中的内存管理

iOS / ManagedObjectContext 中的内存管理

iOS 6 中的 NSTimer 内存管理

读书笔记iOS-属性中的内存管理参数

理解 iOS 的内存管理

ios的内存管理