内存管理题
1.管理原则
1.1内存管理-黄金法则
The basic rule to apply is everything that increases the reference counter with alloc, [mutable]copy[withZone:] or retain is in charge of the corresponding [auto]release.
如果对一个对象使用了alloc、[mutable]copy、retain,那么你必须使用相应的release或者autorelease。
1.2类型定义:
基本类型:任何C的类型,如:int、short、char、long、struct、enum、union等属于基本类型或者结构体;
内存管理对于C语言基本类型无效;
任何继承与NSObject类的对象都属于OC类型。
所有OC对象都有一个计数器,保留着当前被引用的数量。
1.3内存管理对象:
OC的对象:凡是继承于NSObject;
每一个对象都有一个retainCount计数器。表示当前的被应用的计数。如果计数为0,那么就真正的释放这个对象。
alloc、retain、release函数:
1)alloc 函数是创建对象使用,创建完成后计数器为1;只用1次。
2)retain是对一个对象的计数器+1;可以调用多次。
3)release是对一个对象计数器-1;减到0对象就会从内存中释放。
增加对象计数器的三种方式:
1)当明确使用alloc方法来分配对象;
2)当明确使用copy[WithZone:]或者mutableCopy[WithZone:]来copy对象的时;
3)当明确使用retain消息。
上述三种方法使得计数器增加,那么就需要使用[auto]release来明确释放对象,也就是递减计数器。
2. 非ARC模式,MRR(manual retain-release)
MRR内存管理里的一个核心原则,“只负责你拥有的对象的生命周期”,也就是说,如果你对一个对象有所有权,那么你就要负责其回收的工作,否则,你不需要,也不能取回收你不拥有的对象。
1:所有使用alloc, new, copy或mutabelCopy,以及这些关键词开头的函数返回的对象,你都是拥有所有权的,也就是要负责这些对象的内存回收工作。这是iOS开发中的一种约定,所以,当你编写自己的alloc, new或copy类型的函数时,也请遵循这样的命名规范。
2:retain返回的对象,拥有所有权。例如显示调用retain函数返回的结果,或者synthesize 的retain类型的成员变量。
3:所有使用其他函数返回的对象,没有所有权。
4:返回的对象的引用,没有所有权。
5:autorelease返回的对象没有所有权。
2.1 cocoa中的内存管理机制——引用计数
- 引用计数(reference counting)又称为保留计数(retain counting),引用计数的数值表示有几个其它对象在使用它。
- 每一个对象都拥有一个引用计数
- 当对象被创建的时候,引用计数的值为1 当发送retain消息的时候,该对象的引用计数加1,该对象的引用计数为2
- 当这个对象发送release消息的时候,该对象的引用计数减1
- 当一个对象的引用计数为0时,系统自动调用dealloc方法,销毁该对象。
引用计数是实例对象的内存回收唯一参考
引用计数(retainCount)是Objective-C管理对象引用的唯一依据。调用实例的release方法后,此属性减一,减到为零时对象的dealloc方法被自动调用,进行内存回收操作,也就是说我们永不该手动调用对象的dealloc方法。
关于dealloc方法它的作用是,当对象的引用计数为0时,系统会自动调用dealloc方法,回收内存,它的一般写法为:
-(void)dealloc
{
[super dealloc];
[person release];
}
在这里为什么要调用父类的dealloc方法呢?
- 子类的某些实例是继承自父类的,因此,我们需要调用父类的dealloc方法,来释放父类拥有的这些对象。
一般来说调用的顺序是,当子类的对象释放完时,然后再释放父类的所拥有的实例,这一点与调用初始化方法,正好相反。
2.2 对象所有权
当一个所有者(可以是任何一个OC对象)做了以下某个动作的时候,它就拥有了对一个对象的所有权。
-
1,在OC中,对象不断的被其它创建、使用和销毁,为了保证程序不产生额外的内存开销,当对象不再被需要以后,应当被立即销毁。
-
2,拥有对一个对象的使用权,我们称为是”拥有”这个对象。对象的拥有者个数至少为1,对象才得以存在,否则它应当被立即销毁。
-
3,为了确定你(一个对象)何时具有对另外一个对象的所有权,以及明确对象所有者的权利和义务,Cocoa设立了一套标准。只有当你对一个对象做了alloc,copy和retain等操作之后,你才拥有它的所有权。当你不再需要这个对象的时候,你应该释放你对它的所有权。千万不要对你没有所有权的对象进行释放。
//(1)如果创建或者复制某个对象时,则拥有了该对象的所有权,即包含下列关键词时:
alloc,allocWithZone:,copy,copyWithZone:,mutableCopy,mutableCopyWithZone:
//(2)如果没有创建或复制对象,而是保留引用,同样拥有该对象的使用权
retain
//(3)当拥有了某个对象的所有权,在不需要某一个对象时,需要释放他们,用
release,autoRelease
2.3 自动释放池的相关用法
1)cocoa中的自动释放池(Autorelease pool),是能够自动释放赤忠的对象的。NSObject类提供了一个autorelease消息,当我们想一个对象发送autorelease消息的时候,这个对象就会随着释放池的销毁而释放。如果要向使用使用自动释放池释放对象,我们首先要有一个入池操作:
//入池对象5.0之后的写法
//创建自动释放池
@autoreleasepool {
//入池对象5.0之后的写法
}
//入池对象5.0之前写法
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
[pool release];
自动释放池是以栈的形式实现的,当某个对象调用了autorelease方法时,该对象会被加入自动释放池的栈顶。对于发送了autorelease消息的对象,当自动释放池销毁时,自动释放池会对这些对象发送一条release消息,来释放他们。
2)向自动释放池发送release及drain消息的区别
当我们向自动释放池pool发送release消息时,它会向池中的每一个发送了autorelease消息的对象发送一条release消息,并且自身也会销毁。当向它发送drain消息时,只会释放里面吧的对象,而不会销毁自己。
3. ARC模式
当你在编译程序的时候提供自动管理内存的功能,它会自动加入内存的控制代码,控制对象的生命周期,大大简化了内存管理的步骤,ARC管理内容的原理就是,编译器会在适当的地方自动插入retain、release和autorelease消息
3.1 ARC机制下有几个明显的标志:
- 不允许调用对象的 release方法
- 不允许调用 autorelease方法
- 再重写父类的dealloc方法时,不能再调用 [super dealloc];
3.2 ARC的注意点和优点
-
ARC的注意点
- ARC是编译器特性,而不是运行时特性
- ARC不是其它语言中的垃圾回收, 有着本质区别
-
ARC的优点
- 完全消除了手动管理内存的烦琐, 让程序猿更加专注于app的业务
- 基本上能够避免内存泄露
- 有时还能更加快速,因为编译器还可以执行某些优化
3.3 ARC的判断原则
-
ARC的判断原则
- 只要还有一个强指针变量指向对象,对象就会保持在内存中
-
强指针
- 默认所有指针变量都是强指针
- 被__strong修饰的指针
Person *p1 = [[Person alloc] init];
__strong Person *p2 = [[Person alloc] init];
- 弱指针
- 被__weak修饰的指针
__weak Person *p = [[Person alloc] init];
-
注意:当使用ARC的时候,暂时忘记“引用计数器”,因为判断标准变了。