Chapter 17 Memory Management and Automatic Reference Counting

arrayWithContentsOfFile能读取并解析文件并把返回的结果存储进一个新创建的数组中。
Automatic Reference Counting, ARC可以使存储管理自动化。
Objective-C开发者的3个内存管理模型:
1、垃圾自动收集。
2、autorelease pool和手动引用计数。
3、Automatic Reference Counting, ARC
Automatic Garbage Collection
就是自动回收内存嘛,免得内存泄露。
现在的IOS运行时环境不支持自动垃圾回收,OS X也正在减弱对自动垃圾收集的支持,但是OS X推荐你使用ARC。
Manual Reference Counting
就好像C++中的智能指针一样,你初始化一个对象时该对象的引用计数为1,此后每增加一处引用,计数值就加1,这可以通过retain方法实现,如下图所示:


如果某对象你不再使用,你可以使用release方法使计数值减一,如下图所示:

当对象的引用计数为0时就表示该对象已经没用了需要进行内存回收,你可以使用NSObject中的dealloc来释放内存。
如果你的对象比较复杂,它的释放牵涉到其他对象的释放,你需要重写dealloc来完成特定的功能。
像removeObjectAtIndex:和removeFromSuperview这样的方法可以减少对象的引用计数。
如果你用一个指针指向一个已经被销毁的对象,那么该指针被称为野指针,它会引起程序的异常,有时候奔溃。你也不可以对一个已经被销毁的对象再次销毁,否则这通常会导致奔溃。
Object References and the Autorelease Pool
像函数返回值的这种临时性的对象,NSAutoreleasePool负责这种临时性对象的销毁,它提供的方式是autorelease pool。
对象会在这个pool被耗尽时销毁,而pool销毁的过程是通过对pool发送销毁消息实现的。
向pool添加一个对象的方式如下所示:

当你使用framework、uikit或者appkit时会产生大量的临时对象,因此你必须要用autorelease pool,方法如下:
Chapter 17 Memory Management and Automatic Reference Counting_第1张图片

这个语句块是自动产生的,即使你禁用了ARC机制。
当autoreleasepool中最后一条语句执行完后,这个autoreleasepool就枯竭了,然后其中所有的临时对象都将被销毁。autoreleasepool中并不包含实际的对象只是包含对象的引用,它枯竭的时候只是把对象的引用计数清零,然后内存释放的工作交给dealloc去做。
并不是所有的对象都会被添加到autoreleasepool中的,像被以alloc、copy、mutableCopy和new开头的方法创建的对象就不会被自动添加到autoreleasepool中,这被称为你拥有这个对象,这意味着销毁对象释放内存的工作需要你亲自完成。其方法是你向目标对象发送一个release消息,或者你手动把它添加到autoreleasepool中去。

有的时候你在某函数内部必须初始化一个本地对象,而且要将它返回,你还不能马上释放它,于是只能把释放的步骤推迟到autoreleasepool枯竭之时。你可以像如下这样做:


The Event Loop and Memory Allocation
cocoa和IOS应用的内部运行叫一次运行或者事件循环,任何一次运行都会有很多你看不见的处理过程和事件,每个事件的结束都会伴随着autoreleasepool的枯竭,这就意味着临时对象的销毁。如果你想让对象的生命期逃离在autoreleasepool枯竭时被销毁的宿命,你就必须要手动引用计数。
Chapter 17 Memory Management and Automatic Reference Counting_第2张图片

retain关键字说明了setter方法必须保留作为右值赋给属性的那些对象,不过要先把原来的左值对象的值去掉。
因为framework产生的对象在默认情况下实在autoreleasepool中的,这就决定它在一个事件完成之后会被自动释放掉,然而有的时候我们并不希望如此,我们希望那些对象活得长远一些。如果解决这种卸磨杀驴的机制呢?可以通过如下三种方式实现:

它能在逃离autoreleasepool枯竭而被销毁的命运。

这种方法就是压根不把对象放进autoreleasepool中去。

这是使用setter方法。
你重写的dealloc版本如下所示:
Chapter 17 Memory Management and Automatic Reference Counting_第3张图片

super dealloc是用来释放继承下来的所有对象成分的。
在通常的情况下,你要么选择autoreleasepool要么选择分配一个对象。如果你要在时间结束和autoreleasepool枯竭前创建很多对象,那你还是手动创建比较好,因为那样你可以随时用完随时销毁。
在手动引用计数环境下,除了atomic和nonatomic之外,还可以有assign、retain和copy属性。
assign属性如下图所示这样工作:

retain属性如下这样工作:
Chapter 17 Memory Management and Automatic Reference Counting_第4张图片

copy是这样的:
Chapter 17 Memory Management and Automatic Reference Counting_第5张图片

Summary of Manual Memory Management Rules
这里主要说的是在没有ARC和垃圾收集器的环境下要牢记的规则。
1、如果你想保有一个对象,请确保它不会被其他人破坏掉,所以你应该retain它。你用完了记得释放掉它。
2、发送一个release消息并不意味着销毁一个对象。只有当对象的引用计数为0对象才能被销毁。而系统是通过dealloc消息来释放内存的。
3、释放掉每一个用copy、mutableCopy、alloc和new创建的对象,这包括retain和copy属性。并且你需要重写dealloc方法。
4、处于autoreleasepool中的对象会在autoreleasepool枯竭时被销毁。
5、对于在函数内部创建在函数结束时返回的对象,你应该用autorelease来标记它。
6、当应用程序结束时无论啥对象都会被释放。
7、当你在开发IOS和Cocoa时,如果想让对象在autoreleasepool中的生命期跨越事件的结束,你就必须要retain它。
Automatic Reference Counting
ARC清除了所有MRC的隐患,它实现了自动化的引用计数和跟踪,就连释放也不用你管。
Strong Variables
所有的对象指针都是强变量。
那意味着把一个对象指向强变量会使那个对象被自动retain。
旧的对象指针会在赋值前被释放掉。
强变量在默认情况下会被初始化为0,无论它是一个实例或者本地还是全局变量。


这样的赋值会导致f2的值被重写,它对原值的引用丢失,原值无法再被使用,这就是内存泄露了。
如果你是用ARC,那么语句应该是这样子的:

从这个单词就可以看得出来,f2的值会被自动释放掉。
虽然所有的这种对象指针都是强变量,但是你仍然可以显式声明它们,如下图所示:

关键字就是__strong。
属性在默认情况下是unsafe_unretained或者等价的assign,声明强属性的方式如下:

WeakVariables
有的时候你需要让两个对象建立起联系比如一个对象指向了另一个对象。
如果两个对象之间有对彼此的强引用,那么哪个对象都无法被销毁。
解决这种问题的途径是在这两个对象之间用一种不同类型的对象作为一个中间层来指向这两个对象。在这种情况下子对象对父对象的引用就是弱引用,这是因为所谓弱引用就是一个对象对它所属对象的引用,反之则为强引用。
若变量无法阻止它所指对象的空间分配。
当你声明弱变量时有一些事情就悄悄地发生了。系统跟踪赋给该弱变量的引用,当所指对象被分配空间了,弱变量就自动被设置为nil。这防止了因为意外赋值给该对象而造成的奔溃。因为发送给nil任何消息没有效果。
你可以使用__weak来声明弱变量:

弱属性的声明方式如下:

弱变量再应用委托时作用很大,对委托赋予一个弱引用保证了在委托对象在销毁时那个变量被置为0。与此同时,这也保证了系统不发生奔溃。
注意IOS4和MAC OS X 10.6以前的版本不支持弱变量,你仍然可以声明unsafe_unretained属性和变量,不过它不能保证在被引用的对象被销毁时这些变量被置0。
@autoreleasepoolBlocks
在程序中你可能会用到很多autoreleasepool块,如下图所示:
Chapter 17 Memory Management and Automatic Reference Counting_第6张图片

autoreleasepool的应用与ARC无关。
Method Names and Non-ARC Compiled Code
ARC向后兼容,即,它可以对那些没有使用过ARC的代码使用,只要旧的代码遵循了标准Cocoa命名规范。
所谓标准Cocoa命名规范就是名称中除了一个单词外其他单词第一个字母都要大写。如果某些方法没有遵循这一规范,你需要显式地告诉编译器它拥有某一个对象。
如果你生成的属性中包含有alloc、new、copy、mutableCopy或者init,那么编译器会报错。

你可能感兴趣的:(Chapter 17 Memory Management and Automatic Reference Counting)