八---内存管理1


一,为什么要管理内存

在ios开发中对内存的管理是很重要的,因为手机的内存及其有限,一个程序在运行中如果占用过多内存会影响到手机的运行速度,这也就不是一个好的应用程序。那么如何让程序尽可能少的占用内存呢?最核心的思想就是及时回收不用的对象,程序运行中需要产生很多对象以便完成我们需要的活动,对象一旦创建出来就会在内存中占用空间,当这个对象完成活动不再被我们所需要时要及时把它从内存中清除。

系统是怎么判断一个对象什么时候被回收呢?每一个对象内部都存在一个引用计数器,计算这个对象”被引用的次数”,引用计数器占用四个字节存储空间。

二,内存管理基本操作

当使用alloc,new,或者copy创建一个新对象时,新对象的引用计数器默认是1,当对象的引用计数器为0时,系统就会回收它占用的内存。我们可以对对象的引用计数器进行操作:

给对象发送retain消息会使对象的引用计数器值+1;

给对象发送release消息会使对象的引用计数器值-1;

给对象发送retainCount返回对象当前的引用计数器值。

例如:

Person *p =[[Person alloc] init];   //创建一个Person对象,此时p中引用计数器值为1
[Person retain]   //retain返回的是对象本身,p中引用计数器值变成2
[Person release]   //计数器值减1
[Person release]   //计数器值变成0,系统回收p指向的对象内存


此时被回收了内存的Person对象叫做僵尸对象,僵尸对象不能被操作,p指针指向僵尸对象,此时p指针成为野指针。

三,多对象内存管理set方法管理

单个对象的内存管理很简单,复杂的是涉及到多个有联系的对象时的内存管理,例如:

在Person类实现中有个setBook方法使Person类能使用Book类作为成员变量,同时有book方法返回成员变量_book:

@implementation Person
- (void)setBook(Book *)book
{
    _book= book;
}  
- (Book *)book
{
    return_book;
}     //类的声明等其他创建类细节在此省略
Book *b = [[Book alloc] init];
Person *p = [[Person alloc] init];
[p setBook:b];    //内存中的Book对象同时被b和p使用
[b release];  
[p book];    //在此当p再调用book方法就会报错,因为在上一步Book对象已经被回收内存,成为了僵尸对象,不能再被调用


由此引出多对象内存管理的规范:

1,想要使用(占用)某个对象时应对对象进行一次retain操作

2,不再使用(占用)某个对象时应对对象进行一次release操作

3,即:谁retain谁release

因此应对set方法进行初步规范:

- (void)setBook(Book *)book
{
    _book= [book retain];   //想要使用对象时需进行一次retain操作
}  


但是这样的话:

Book *b = [[Book alloc] init];   //b被创建引用计数器值为1
Person *p = [[Person alloc] init];   //p被创建引用计数器值为1
 
[p setBook:b];     //b被p引用,在set方法中b做一次retain引用计数器值为2
[b release];      //b的引用计数器值减1变成1
[p release];     //p的引用计数器值减1变成0被回收


可见b指向的Book对象并没有被回收,造成这样的原因从内存管理规范中可以看出,谁retain谁release,p对象的set方法中对b进行了一次retain却没有相应的release操作。怎么解决这个问题呢?对象在被系统回收内存时,系统会自动调用一次对象自带的dealloc方法,我们要做的是在类的实现中重写dealloc方法:

- (void)dealloc
{
    [_book release];   //当一个对象被回收时,就再用不到成员变量中的类对象,应对成员变量中的类对象进行一次release操作
    [superrelease];   //重写dealloc方法时不要忘记要在最后调用父类的dealloc方法
}


这时候观察发现问题:p对象中的set方法每调用一次,就会对成员变量所指向的b对象进行一次retain操作,若p的set方法被多次调用,即不小心多次输入或用set方法改变成员变量指向的对象,如:

Book *b1 = [[Book alloc] init];   //b1被创建引用计数器值为1
Book *b2 = [[Book alloc] init];   //b2被创建引用计数器值为1
Person *p = [[Person alloc] init];  //p被创建引用计数器值为1
[p setBook:b1];     //b1被p引用,在set方法中b1做一次retain引用计数器值为2
[p setBook:b2];     //p中的成员变量改变指向了b2,在set方法中b2做一次retain引用计数器值为2
 [b1 release];      //b1的引用计数器值减1变成1
[b2 release];      //b2的引用计数器值减1变成1
[p release];     //p的引用计数器值减1变成0,内存被回收时系统调用我们重写的dealloc方法,对此时成员变量指向的b2对象进行一次release操作,b2引用计数器值变成0也被系统回收


我们发现b1并没有被回收,因此我们在进行set方法中的retain操作之前应进行一次判定,判定成员是否改变了,set方法代码最终完善为:

- (void)setBook(Book *)book
{
     if( book != _book )   //1,先判断传进来的是不是新对象
     {
           [_bookrelease];   //2,对旧对象进行一次release
           _book = [bookretain];   //3,对新对象进行一次retain
     }
}  


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值