内存管理Part 1
内存管理是一件极其简单又极其麻烦的事情。
说它简单,因为所谓的内存管理不过就是两件事情:一块内存我们是要用,还是不要用;该用的时候就用,不用的时候就释放。麻烦的地方就在于我们很容易疏忽,而造成用与不用不成对。这就很像开车,开车也不过就是前进与刹车,但是天天路上都会发生车祸。
不成对,就会造成两种结果:如果用完之后不释放内存空间,就会造成程序占用了一堆没有用到的内存空间,内存空间愈来愈大,最终造成内存用尽,在iOS上系统会强制杀死我们的应用程序,这种状况叫做内存泄漏(Memory Leak)。
反之,如果一块内存已经被释放掉了,我们却还以为这块内存还存在我们可以调用的实例,所以当我们尝试调用的时候,才发现这块内存存放的实例已经不在了,这种状况叫做over-release或是invalid memory reference,会造成应用程序crash,crash log上面会告诉你错误类型是EXC_BAD_ACCESS
。
处理over-release 可能是初学者开始学习Mac OS X 与iOS平台的第一个障碍(我觉得通常第二个障碍是搞不懂delegate是什么):一开始写程式,程式却一直莫名其妙的crash,于是前往各大网络论坛求救,但是通常也不会有多少人帮忙,因为网路上的其他同行通常都乐于回答问题,但懒得帮别人修bug。
过去三十年学界与业界也一直努力解决内存管理的问题,毕竟在写代码的时候,不把力气放在处理程序逻辑问题,而是这种琐碎的困扰,对于工程师的生产力是一大伤害,主要提出的解决方案是内存自动回收机制(Garbage Collection,GC),在程序执行时,如果发现已经没有任何一个变数指向某块内存,就代表这块内存再也用不到,于是开始回收这块内存。90年代之后诞生的计算机语言,几乎都有GC 机制。
苹果曾经推出Mac OS X 10.5 时,在Mac OS X 上实现了GC,同时也带动一些使用GC 的动态语言开始与Cocoa Framework 桥接,诞生了PyObjC、MacRuby、 RubyCocoa、JSTalk 项目,我们因此也可以使用Python、Ruby、JavaScript 等语言,直接撰写Mac OS X应用程序。但是,苹果所实现的GC,问题不小。
但我过去参与一个项目,在Mac OS X 10.6上面以GC开发一个软体,结果可说是凄惨无比:整个Cocoa Framework是架构在很多C library上的,而底层许多library其实并不支持GC,我们发现只要在main thread之外用到像是NSDateFormatter
、NSNumberFormatter
这些class(这些class架构在libICU上,是IBM的一项Open Source专案,用来处理多国语系的格式问题,同时也是一套Regular Expression引擎),GC就完全没有作用,内存疯狂泄漏。苹果在推出iOS之后,也始终不在iOS上实现GC。
随着苹果在Compiler 的投资逐渐展露成果,逐步将Compiler 从GCC 换成LLVM,最后决定改变技术方向,从另外一种方向来解决将内存管理自动化的问题,就是在iOS 5 上推出的ARC(Automatic Reference Counting),不在runtime回收内存,而是在编译程式的时候,自动帮你加上与内存释放有关的代码。而苹果的下一步,就是直接在ARC 的基础上开发新的程序语言Swift。
虽然ARC推出的目的就是希望你以后再也不要为内存管理问题烦恼,但问题是,实际上,你还是得知道内存管理是怎么运作的。—记得有人打比方说,GC是自动档车,而Objective-C的记忆体管理就像开手动档车,要自己知道怎么进档对档,我觉得,加上ARC呢,其实还是手排车,只是你训练了一只猴子帮你在副驾驶座帮你打档,而你接下来,还得要学会怎么调教这一只猴子。
如果你在开发iOS 或Mac App 的时候,真的完全不想知道Objective-C 的内存管理细节,可能就直接写Swift 吧!