【iOS面试粮食】内存管理

1,268 阅读4分钟

本文章将记录Objective-C中内存管理的相关资料,如有错误欢迎指出~

iOS的内存管理一般指的是OC对象的内存管理,因为OC对象分配在堆内存,堆内存需要程序员自己去动态分配和回收;基础数据类型(非OC对象)则分配在栈内存中,超过作用域就会由系统检测回收。如果我们在开发过程中,对内存管理得不到位,就有可能造成内存泄露。

Objective-C中提供了两种内存管理机制:MRC(MannulReference Counting)ARC(Automatic Reference Counting)MRC指的是手动内存管理,在开发过程中需要开发者手动去编写内存管理的代码ARC指的是自动内存管理,在此内存管理模式下由LLVM编译器和OC运行时库生成相应内存管理的代码

引用计数

不管是MRC 还是 ARC ,都是通过对引用计数来进行内存管理的。

  • 创建一个新对象的时候,它的引用计数为 1
  • 当有一个新的指针指向这个对象时,其引用计数加 1
  • 当某个指针不再指向这个对象是,其引用计数减 1,当对象的引用计数变为 0 时,说明这个对象不再被任何指针指向了,这个时候就可以将对象销毁,回收内存。

memory-ref-count.jpg

当一个对象使用完没有释放,此时其引用计数永远大于1。该对象就会一直占用其分配在堆内存的空间,就会导致内存泄露。内存泄露到一定程度有可能导致内存溢出,进而导致程序崩溃。

MRC(MannulReference Counting)

先了解下 内存管理的思想

  • 自己生成的对象,自己持有。
  • 非自己生成的对象,自己也能持有。
  • 不再需要自己持有的对象时释放对象。
  • 非自己持有的对象无法释放。

从上面的思想来看,我们对对象的操作可以分为三种:创建,持有,释放,再加上废弃,一共有四种。它们所对应的Objective-C的方法和引用计数的变化是:

对象操作 Objecctive-C方法 引用计数的变化
生成并持有对象 alloc/new/copy/mutableCopy等方法 +1
持有对象 retain方法 +1
释放对象 release方法 -1
废弃对象 dealloc方法

ARC (Automatic Reference Counting)

ARC机制下,编译器就可以自动进行内存管理,减少了开发的工作量。但是仍有一些问题需要我们去注意。

循环引用(Reference Cycle)问题

引用计数这种管理内存的方式虽然很简单,但是有一个比较大的瑕疵,即它不能很好的解决循环引用问题。如下图所示:

  • 对象 A 和对象 B,相互引用了对方作为自己的成员变量,只有当自己销毁时,才会将成员变量的引用计数减 1。
  • 因为对象 A 的销毁依赖于对象 B 销毁,而对象 B 的销毁与依赖于对象 A 的销毁,这样就造成了我们称之为循环引用(Reference Cycle)的问题
  • 这两个对象即使在外界已经没有任何指针能够访问到它们了,它们也无法被释放。

memory-cycle-1.jpg

不止两对象存在循环引用问题,多个对象依次持有对方,形式一个环状,也可以造成循环引用问题,而且在真实编程环境中,环越大就越难被发现。下图是 4 个对象形成的循环引用问题。

memory-cycle-2.png

那该怎么解决循环引用的问题呢?使用弱引用 (weak reference) 的办法。

弱引用

使用弱引用来持有对象,弱引用虽然持有对象,但是并不增加引用计数,这样就避免了循环引用的产生。

在 iOS 开发中,弱引用通常在 delegate 模式中使用。举个例子来说,

  • 两个 ViewController A 和 B,ViewController A 需要弹出 ViewController B,让用户输入一些内容,当用户输入完成后,ViewController B 需要将内容返回给 ViewController A。
  • 这个时候,View Controller 的 delegate 成员变量通常是一个弱引用,以避免两个 ViewController 相互引用对方造成循环引用问题,

memory-cycle-4.jpg

弱引用的实现原理是:

  • 系统对于每一个有弱引用的对象,都维护一个表来记录它所有的弱引用的指针地址。
  • 当一个对象的引用计数为 0 时,系统就通过这张表,找到所有的弱引用指针,继而把它们都置成 nil。

面试题

面试题请参考 这篇文章 iOS 内存管理相关面试题

参考

理解 iOS 的内存管理

《Objective-C 高级编程》干货三部曲(一):引用计数篇

iOS之从MRC到ARC内存管理详解