《深层次探索系统的内存管理(mrc至arc)》
说明:本文为学习笔记,参考书籍《Objective-C高级编程ios与osx多线程与内存管理》,仅供自我学习。
大家看到内存的第一反应就会想到自动引用计数,其实引用计数式内存管理仅仅是思考方式而已。我们需要理解4个词“生成”“持有”“释放”“销毁”。
自己持有的对象,一旦不再需要,持有者有义务释放该对象,释放时调用release方法。对象如果被释放掉了就绝对不能被访问。这也是为block要在堆上面的原因。
自己生成而非自己所持有对象,若用retain方法变为自己持有,也同样可以用release方法释放掉。
使用autrorelease方法,可以取的对象,但自己不持有对象。autorelease提供这样的功能,使对象在超出指定的生存范围时能够自动并正确的释放(调用release方法)。
自己不能释放非自己持有的对象。一旦释放会导致奔溃。
下面我们直接看代码。
NSAllocateObject函数通过调用NSZoneMalloc函数来分配存放对象所需的内存空间,之后将该内存空间置0,最后返回作为对象而使用的指针。
对象的引用计数可以通过retainCount实例方法获得。引用计数为0时,调用dealloc方法来销毁对象。
引用计数表是一个散列表。
autrorelease当前超出作用域(相当于变量作用域时),对象实例的release实例方法被调用。
NSAutoreleasePool对象的生存周期相当于C语言变量的作用域。对于所有调用过NSAutorelease实例方法的对象,在NSAutorepleasePool对象时,都将调用release方法。用源码表示如下图:
在Cocoa框架中,相当于程序主循环的NSRunLoop或者在其他程序可运行的地方,对NSAutoreleasePool对象进行生成,持有和销毁处理。因此应用程序开发者并不一定得使用NSAutoreleasePool对象来进行开发工作。
NSRunLoop每次循环对过程都会不断执行一个操作。生成NSAutoreleasePool对象,应用主程序处理销毁NSAutoreleasePool对象。
尽管如此,但在大量长生autorelease的对象时,只要不销毁NSAutoreleasePool对象,那么生成的对象就不会被释放,因此有时会产生内存不足的现象。比如读入大量图像的同时改变其尺寸。图像文件读入到NSData对象,并从中生成UIImage对象,改成尺寸后生成新的autorelease的对象。由于没有销毁NSAutoreleasePool对象,最终会导致内存不足。
解决方法,有必要在适当的地方生成,持有或者销毁NSAutoreleasePool对象。
通常在使用OC中,也就是Foundation框架时,无论调用哪一个对象的autorelease实例方法,实现上是调用的都是NSObject类的aurorelease实例方法。但是对于NSAutoreleasePool类,autorelease实例方法已经被该类重载,因此运行时会报错。
在ARC中也是遵循下面的规则的:
因此新引入4个属性修饰符:
__strong 修饰符是id类型和对象类型默认的所有权修饰符。也就是说,id变量,实际上是被附加了所有权修饰符。
接下来我们看看__weak,其存在主要是为了解决相互持有造成内存泄漏的问题。
接下来我们看看__unsafe_unretained修饰符:
接下来我们看看__autoreleasing修饰符
使用__week修饰的对象必定被注册到authoreleasepool中的对象。
在iOS应用程序模版中,像下面的main函数一样,@authoreleasepool块包含了全部程序。
NSRunLoop等实现不论是否ARC,均能随时释放注册到autoreleasepoo;中的对象 。
无论是否ARC,只要对象的所有者不持有该对象,该对象就被销毁,也就是走dealloc方法。
另外,在不是ARC无效时必须要调用[super dealloc]。ARC的时候不必书写这个。
另外Core Founation对象框架生成的API生成并持有的对象可以用CoreFoundation框架的API进行释放。