iOS 5中最大的变化时增加了自动引用计数,即ARC。ARC是新LLVM 3.0编译器的一项功能,并且完全废除了所有开发者所讨厌的手动内存管理。
在你自己的projects中使用ARC是十分简单的。像平常一样保持编写,不过不再需要调用retain、release和autorelease。
由于ARC的使用,编译器会在正确的地方自动插入retain, release and autorelease。因为编译器会帮你完成这项工作,所以你无需当心这方面的事情。
How It Works
你大概对手动内存管理已经有一定了解,大概像下面这样工作:
- 如果你需要保持一个对象,你需要保留它,除非它已经为你保留了。
- 如果你想要停止使用一个对象,你需要释放它,除非它已经释放(或自动释放)。
作为一个初学者,你会觉得在头脑里形成这观念比较困难。但是一段时间后,它会成为你的第二天性。现在,你会平衡保留和释放。除非你已经忘却了。
虽然手动内存管理的原理不那么困难,但是却容易造成错误。并且这些小错误会造成可怕的后果。你的程序可能会崩溃,因为你太频繁地释放一个对象或者你的变量指向一个无效的数据。你会耗尽内存因为你没有释放足够的对象。
Xcode的静态分析能够发现这些类型的问题但是ARC已经走得更远了。ARC通过自动插入合适的retains和release来避免内存管理的问题。
发现ARC是Objective-C编译器的功能是重要的。因此,当你构建你的app时,所有的ARC将会出现在里面。ARC既不是一个运行时功能(除了一小部分,弱指针系统)也不是你从其他语言中知道的垃圾回收机制。
所有ARC做的,就是在代码编译时插入retains和release,特别你会插入的地方。或者至少你会自己把它们加进去。这使得ARC能像手动管理内存一样迅速,有时甚至更快。因为它在遮罩下执行了某些优化。
Pointers Keep Objects Alive
ARC的新规则学起来十分简单。在手动内存管理中你需要retain一个对象来保持存活。这是不必要的。你所需要做的是让一个指针指向该对象,只要有一个变量指向对象,该对象就会一直在内存中存在。当指针获取一个新值或者停止存在,相关的对象就会被释放。这对所有变量:实例变量,合成属性甚至是本地变量来说都是正确的。当你像下面这样做的时候:
|
firstName变量变成一个指向保存有文本内容的NSString对象的指针。然后firstName变量现在是该string对象的拥有者。
一个对象可以有多个拥有者。直到用户改变UITextField的内容时,它的文本属性也是string对象的拥有者。有两个指针保持着同一个string对象。
片刻后,用户将会在文本里输入新的内容,现在它的文本指向新的string对象。但是原先的string对象仍然有一个拥有者(firstName变量)并存在在内存中。
只有当firstName也得到一个新值,或者超出范围--因为它是一个本地变量并且它的方法结束了,或者因为它是一个实例变量并且它所属的对象被释放了--到所有权终止为止。string对象不再拥有拥有者,它的保留计数变成0,对象将会被释放。
我们把类似firstName和textField.text的指针称为“strong”,因为他们保持对象存活。默认情况下所有的实例变量和本地变量。
下面是一个“弱”指针。弱的变量仍然可以指向对象,但是不能变成拥有者。
|
weakName指向了和textField相同的string对象,但是它并不是拥有者。如果textField的内容变了,那么string对象就没有拥有者并且会被释放:
当这种情况发生时,weakName的值会自动变成空。也可以说是"零"弱指针。
注意这是极其方便的,因为它避免了弱指针从内存从被释放。虽然这种事情曾引起一系列错误--你可能听说过“悬挂指针”或者“僵尸”--但是由于这些零弱指针,这不再是问题。
你可能不会太常使用弱指针。当两个对象有父子类的关系时,弱指针是非常有用的。父类将有一个指向子类的强指针--然后拥有子类,但是为了防止所有权周期,子类只能有一个指向父类的弱指针。
委托是这方面的一个例子。你的视图控制器可能通过一个强指针拥有一个UITableView。表格视图的数据来源和委托指针指回视图控制器,但是这是一个弱指针。
注意下面的不是很有用的:
|
string对象没有拥有者(因为str是弱的),对象会在创造后马上被释放。当你这样做时,Xcode会给你一个警告,因为这可能是你所没预料到的(“Warning: assigning retained object to weak variable; object will be released after assignment”)。
你可以使用 __strong关键字来指定一个强指针。
|
但是因为默认情况下变量时强的,所以这显得有点多余。.
Properties也可以设置为强和弱. 属性的符号是:
|
ARC是好用的并可以从你的代码中删除一些杂七杂八的。你不再需要考虑什么时候是retain,什么时候应该release,仅仅考虑你的对象应该怎样和每一个对象建立联系。你需要问问自己的是谁拥有着什么。
举个例子,在此之前,你可能会写出这样的代码:
|
在手动内存管理下,从数组移除对象将会使得obj的内容无效化。对象会马上被释放,当它不再是数组的一部分时。用NSLog()打印对象可能会使你的程序崩溃。在ARC里,上面的代码就会如计划中运行。因为我们把对象放在强指针obj里面,数组不再是对象的唯一主人。甚至当我们从数组中移除对象时,对象仍然存活。因为obj始终指向它。
Automatic Reference Counting也有着一些限制。首先,ARC只能用于Objective-C中。如果你的应用程序使用Core Foundation或者malloc()和free(), 那么你仍需负责内存管理。晚些我们将会看到这样的例子。另外,精确的语言规则严格确保ARC能够较好完成工作。这只是小小的牺牲而已,你获得的将会比你放弃的要多得多。
ARC只会为你在适当的地方考虑保留或释放,这并不意味着你可以完全把内存管理置之不理。因为强指针保持对象存活,但有的地方你需要手动把这些指针设置为nil,或者你的应用程序可能会耗尽可用的内存。如果你保持你创建过的所有对象,那么ARC将永远不会释放它们。因此,不管什么时候你创建了一个新的对象,你仍然需要考虑谁将拥有它,该对象的生命周期有多长。
毫无疑问的是ARC是Objective-C的未来。苹果鼓励开发者放弃手动内存管理并且开始使用ARC来编写他们的应用。这是简单和功能更强大的应用程序源代码。有了ARC,内存相关的程序崩溃就变成了历史了。
但是因为我们是出于手动到自动内存管理的一个过渡时期,你将经常遇到不能与ARC兼容的代码,不管它是你自己的还是第三方库的。幸运的是你可以在同一project中结合两者。接下来会展示几种方法来告诉你ARC甚至可以和C++完美结合。你也可以在iOS 4上使用ARC,但这有一些限制。