3. objC内存管理(<ios4.0)

          在 objC中,实例对象的内存管理是靠引用计数来进行的,当前上下文用此对象时,就加一,否则就减一,当减到零时,就析构该对象。

     1 实例对象引用计数加1

     1.1 在一个生命周期中,如果用alloc, new, copy来创建类对象后,那么retain的个数就会变为1,而且此生命周期要负责对此对象发送release或者autorelease消息。这是一个ObjC中默认的规则,因为,客户程序要默认你写的代码是遵循这条规则的。

     1.2 在一个对象生成后,只要向其实例发送retain消息,那么,同样retainCount的个数会增加1

此条规则说起来,非常简单,实际上在编程中,存在诸多陷阱。(c++也同样存在这种情况,见Effective C++)

首先,我们来讲@property,和C#一样,ObjC编译器也同样为属性@property <porpertyA> 生成了-(void) set<propertyA>:<propertyA*> ;-(<propertyA*>) <propertyA>;两个函数。那么,set<propertyA>:<propertyA*>函数具体的生成情况,又按照@property的相关属性进行生成,这些属性可按照下表进行理解

retain/assign/copy
控制生成相应版本的set<propertyA>:<propertyA*>函数体的代码生成
readwrite/ readonly控制是否要生成set<propertyA>:<protertyA*>函数体
atonicity/nonatomic是否在两个函数中,要添加线程同步锁

从上边的属性的描述来看,属性的生成绝对不是想当然那样的简单,如果,程序员没有十足的把握,那么,请把着看似平淡无奇实则高胜莫测的代码交给编译器去作罢。

     因为,这里我们讲解retainCount,可以简单的用代码解析一下简单的retain属性。请看一下代码:

@interface Patient:NSObject
@property(retain, nonatomic) NSString* Name;
@end
@implementation Patient
@end

@interface WorkContext:Patient
{
    Patient* patient;
}
@property(retain, nonatomic) Patient* patient;
/*
-(void) setPatient:(Patient *)patient;//属性默认生成的两个函数,
-(Patient*) patient;*/
-(void) assign2Patient:(Patient*) pat;//模拟(void) setPatient:(Patient*) patient;
@end
@implementation WorkContext
@synthesize patient;
/*
-(void) setPatient:(Patient *)patient
{
    int a = 0;
}
-(Patient*) patient
{
    return nil;
}*/
-(void) assign2Patient:(Patient *)pat//如果自己不是很擅长维护类对象的次数,请交给编译器吧
{
    Patient* ptemp = patient;//防止自己赋值自己
    patient = [pat retain];
    [ptemp release];//和C++中不同,无需判断是否为空。
}
@end
/**调用
*/
    Patient* patient1 = [[Patient alloc]init];
    NSLog(@"%ld", [patient1 retainCount]);
    WorkContext* workcontext = [[WorkContext alloc]init];
    NSLog(@"%ld",[workcontext retainCount]);
    workcontext.patient = patient1;//属性的赋值
    NSLog(@"%ld", [patient1 retainCount]);  
    [workcontext setPatient:patient1];//属性默认生成的set参数,同一个对象,赋值两次
    NSLog(@"%ld", [patient1 retainCount]);
    [workcontext setPatient:[workcontext patient]];//自己向自己赋值
    NSLog(@"%ld", [[workcontext patient] retainCount]);
    
    
    //练习2
    Patient* patient2 = [[Patient alloc]init];
    WorkContext* workcontext2 = [[WorkContext alloc]init];
    [workcontext2 assign2Patient:patient2];//模拟赋值函数
    NSLog(@"%ld",[patient2 retainCount]);


2 实例对象引用计数减1

            在Objective-C和C++中,都存在一个不成文的规定,谁申请的内存还是有谁释放。这里的谁实际就是作用域,在当前的作用域中申请了内存空间,必须要在此作用域中进行释放。

         这样就出问题了。

         因为,在编写函数的过程中,常常会出现在一个函数中申请内存,而此内存要在函数体之外进行应用的情况,然后由调用函数体对内存进行释放。这就有悖上文提到的不成文的规定。

        这样autorelease就产生了。

         函数调用release或autorelease的方法来向运行时表示对象在当前的上下文中不在需要。其中,release的方法是直接减一,并判断,如果retainCount现在为零,那么就释放该对象。

         autorelease是表示该实例对象,在当前的上下文中不再需要了,retainCount保持不变。待到 NSAutoreleasePool进行清理的时候,要对所有登记过的对象进行判断。如果对象的retainCount的值和登记次数相等,那么就对该对象进行释放。

         实际上这两种方式没有本质上的区别,都是表示该对象在当前上下文中,不再需要。只有释放内存的时机不同。

         autorelease在编程中,非常之有用。因为程序员可以用它可以表述当前的实例对象,在当前的执行上下文中,已完全没有用处了。但在其他的函数或模块,可能有用。例如,上个章节《2.objC动态绑定》代码中的@implementation PaitentCreator工厂类的作用,用来生成一个实例对象。这一点,要比C++做的要好,C++中工厂类所生成的对象,常常是由调用者去负责销毁,直接delete或调用服务代码提供的函数,无论以哪种方式,把销毁对象的任务交给客户代码,都太危险了,大大增加内存泄漏的风险。下边是对内存管理进行的测试代码

NSAutoreleasePool* pool= [[NSAutoreleasePoolalloc] init];
    Patient* tempPat = [[PaitentCreatorCreatePatient] retain];
    NSLog(@"the tempPat instance object retain count = %ld", [tempPat retainCount]);//2
    /**
     */
    NSMutableString* pString = [[NSMutableStringalloc]initWithString:@"Zhao^Tiegui^^^"];
    NSLog(@"the current name retain count = %ld",[pString retainCount]);//1
    tempPat.name = pString;
    NSLog(@"the current name retain count = %ld",[pString retainCount]);//2
    [pString release];
    NSLog(@"the current name retain count = %ld",[pString retainCount]);//1
    [tempPat printOut];
    /**
     */
    NSString* tempSex = [[NSStringalloc] initWithCString:"Zhao^Tiegui"];
    NSLog(@"the sex sex retain count = %ld",[tempSex retainCount]);//1
    tempPat.sex = tempSex;
    NSLog(@"the sex sex retain count = %ld",[tempSex retainCount]);//2
    [tempSex release];
    NSLog(@"the sex sex retain count = %ld",[tempSex retainCount]);//1
    [tempPat printOut];
  
    NSLog(@"the tempPat retain count = %ld",[tempPat retainCount]);//2
    [tempPat release];
    NSLog(@"the tempPat retain count = %ld",[tempPat retainCount]);//1
    [pool release];
    NSLog(@"the pool retain count = %ld",[pool retainCount]);//1
    NSLog(@"the tempPat retain count = %ld",[tempPat retainCount]);//1152921504606846975无效的值
    [tempPat printOut];//产生异常

         这里要特别的注意,在新建的类中,一定要重载dealloc函数。并且,super的dealloc要放在子类释放之后,这里和C++析构的顺序是相同的。具体原因一样,子类在析构时候,可能会调用父类的函数。所以,父类一定要后析构于子类。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值