内存管理与引用计数器机制
1.内存管理:当程序需要空间,则手动分配空间。当空间或其他资源不再需要时,手动回收。
2.内存管理的必要性:
当涉及ObjC的根类NSObject或其派生类时,我们往往会使用alloc方法申请内存,通过向对象发送release消息回收内存。但是,事情不总是这么简单。正在运行的应用程序可能在多个地方会引用你创建的对象;比如,一个对象可以被存储在一个数组中或者在其它地方被一个实例变量所引用。这种情况下,除非你确定每一个引用该对象的使用者,都不再使用该对象了,否则,你就有可能没办法去释放对象所占的内存,如果贸然回收空间还可能会产生内存泄漏或者二次删除的问题。此时,就需要我们对内存进行管理
3.C或者C++中与oc内存释放的区别
内存申请一次就释放一次,虽然多个指针可以指向同一块空间,但是释放时只能通过其中一个指针回收这块空间(c或c++中)
增加了“引用计数器机制”来记录该对象被引用的次数,对象被引用几次,就需要释放几次(oc中)
4.oc中内存管理的原理
OC中对内存的管理是依赖对象引用计数器来进行的:在OC中对象的声明方式都是为一个指针分配对象堆空间。在每一个OC对象内部都有一个与之对应的整数变量(retainCount),叫做“引用计数器”,当一个对象在创建之后它的引用计数器值为1(OC中调用一个对象的方法就是给这个对象发送消息),当调用这个对象的release方法之后它的引用计数器减一,如果一个对象的引用计数器值为0,则系统就会自动调用这个对象的dealloc方法来销毁这个对象
5.ARC机制
在Xcode4.2以及以后的版本中由于引入了ARC(Automatic Reference Counting)自动引用计数机制,程序编译时Xcode可以自动给你的代码添加内存释放代码,如果编写手动释放代码Xcode会报错,如果使用是Xcode4.2以后的版本,必须手动关闭ARC。
ObjC中的内存管理机制跟C语言中指针的内容是同样重要的,要开发一个程序并不难,但是优秀的程序则更测重于内存管理,它们往往占用内存更少,运行更加流畅。
虽然在新版Xcode引入了ARC,但是很多时候它并不能完全解决你的问题。在Xcode中关闭ARC:项目属性—Build Settings--搜索“garbage”找到Objective-C Automatic Reference Counting设置为No即可。
注意:
AClass* anObject = [[AClass alloc] init]; // reference count = 1 after alloc
[anObject retain]; // reference count += 1 after retain //2
[anObject release]; // reference count -= 1 after release //1
[anObject release]; // reference count == 0 then dealloc //0
5.我们可以通过向对象发送retainCount消息获得对象引用计数器的值;
5 、(1)在OC中当仅仅声明一个对象指针而不进行alloc分配内存的话,那么这个对象指针的引用计数器的值retainCount=0;引用计数器就是一个名为retainCount的整型变量,查看方式为:[对象指针 retainCount];
(2)当声明一个对象并且用alloc分配内存init进行初始化后,retainCount的值为1,然后每调用一次retain 其引用计数器的值加一,每调用一次release方法 其retainCount的值减一,当理论上计数器的值为0时 系统就会调用对象的dealloc方法来对对象内存进行释放。
(3)当retain的release一定的次数使其理论值为0时系统虽然调用它的dealloc方法对对象的堆内存进行释放,但当释放内存后 调用输出它的引用计数器的值即retainCount还是为一,但只限且仅限一次也就是说释放内存后系统会给计数器一次输出机会但输出的值不为0 而是一。当输出一次之后,再调用引用计数器输出retainCount的值就会变成一个错误的大型数值,可以认为这个错误数字是个未知的地址。但是释放内存后如果没有给对象指针赋值为nil,再调用对象的任何成员及其方法都会造成错误。
(4)当对象指针调用足够次数的release方法激发dealloc方法释放内存后,若给对象指针赋值为nil。无论调用对象指针的任何成员以及任何方法包括(retain和release)方法都不会造成错误。但是都不再起任何的作用,因为它的内存已经释放并且被标示为空。此时调用它的任何setter方法或其他方法都不能操作数据,因为它的对象已经为空。此时输出它的任何整型数值都为0,任何别的类型值都为“ (null)”。
(5)如果对象指针没有调用足够的次数就不能激发对象的dealloc方法进行内存释放,若在指针所指向的对象空间没有被释放之前为为对象指针赋值为nil,虽然能够运行但是会造成内存泄露。无论什么情况下为对象指针赋值为nil,其情形都与(4)中相同。
(6)注意:如果对象指针以retain的方式赋值给另一个指针,内容上完全是指针值拷贝赋值,但是他们的引用计数器的变量retainCount的值会自增一。就等于说同一对象空间的地址给多个对象指针赋值,retain方式进行对象指针赋值等于把同一对象绑定多个指针,有任何一个指针没有解除绑定系统就不会调用dealloc方法来释放对象内存。引用计数器的变量retainCount存的就是绑定对象的指针个数。切记:retainCount是这些指针共享的一个变量,也就是说同一对象以retain赋值绑定的任何指针调用引用计数器时,都是同一个retainCount,所以值都是相同的。
6、已知在@property和@synthesize配套生成成员的setter/getter 方法中,就有retain和copy的用法,切记retain是针对于一般的Object(对象)的,而copy(分配新堆首地址且所指向堆的内容完全拷贝)是针对于NSString字符串对象的。假设一个复合类Person类中有一个成员“NSString *name;”和另一个自定义的普通类"Computer *c;"作为成员。
自动生成setter/getter方法时就需要用以下语句:
@property(nonatomic,copy)Nsstring * name;
@property(nonatomic,retain)Computer *c;
----------------------------------------------------
@synthesize name,c;
如果要手动提供代码,不使用@自动生成set/get方法时,就等于以下代码:
-(void)setName:(NSString *) _name;
-(NSString *)name;
-(void)setC:(Computer *)_c;
-(Computer *)c;
----------------------------------------------------
-(void)setName:(NSString *)_name
{
if(_name!=name){
[name release];
name=[_name copy];
}
}
-(NSString *)name
{
return name;
}
-(void)setC:(Computer *)_c
{
if(c!=_c){
[c release];
c=[_c retain];
}
}
-(Computer *)c
{
return c;
}
注意:(一)如果没有判断防止自身赋值的if语句,很肯能在第一句release后就把自身内存释放了,两个相同的空指针是无法完成retain操作的。(二) 如果没有release语句,若原先成员指向的有堆空间的话直接赋值就会造成原先的内存泄露。(三)即使成员指针没有指向堆内存,仅仅对定义的指针release也不会出错,因为未初始化之前,它是nil值。
一個復合類中的release