init和dealloc中使用property(accessor)的副作用

40 篇文章 0 订阅
32 篇文章 0 订阅

本文受Ash Mike的《The complete Friday Q&A:Volume I》的启发所编写。


accessor最大的优点是在数据访存安全性的保证,同时还可以完成内存管理功能,对于override的accessor还加入了一些自定义的功能,非常便捷。但是反对者也提出来在init和dealloc中使用accessor所带来的副作用。苹果官方文档也建议不要在这里面使用accessor。

Ash Mike提出了一种场合,在init和dealloc中使用accessor不慎,可能导致的问题:

假设我们在子类里覆盖了父类的setter方法,并在里面使用父类的setter方法对父类完成一些初始化或者销毁变量的操作。下面的例子看起来无害,却有一些潜在的问题:

-(void)setSomeObj:(id)obj
{
        [anotherObj notifySomething];
        [super setSomeObj:obj];
}

-(void)dealloc
{
         [anotherObj release];
         [super dealloc];
}

假如父类使用accessor销毁someObj,那么覆盖的setter会在dealloc执行完之后被调用,但是此时anotherObj已经被release,这样就潜在问题,也就是anotherObj指向一块不能保证安全性的区域,是个dangling reference。虽然anotherObj指向的区域不会立即被销毁或者被别的对象所使用,但是是潜在的即将被回收使用的。出于安全的考虑,我们应当在[anotherObj release];之后将其设置为nil:anotherObj = nil;。


至于整个过程是怎么回事的,就涉及面向对象的基本知识了。当子类release之后,就会调用子类的dealloc,anotherObj被release,然后调用父类的dealloc,先前我们说父类的dealloc应该类似这样:

-(void)dealloc
{
        [self setSomeObj:nil];
        [super dealloc];
}

这里应该注意的是父类调用的setSomeObj:是被子类覆盖的那个,因此anotherObj 又执行一个方法,当然这是不安全的,可能会导致crash,假如像前面那样设置为nil的话,这里相当于什么都没做。接着再执行super 的setSomeObj:方法,这时候才是调用父类自己的setter。setter的合成写法大概类似下面:

-(void)setSomeObj:(id)obj
{
    [obj retain];
    [someObj release];
    someObj = obj;
}

这样会先对nil进行retain,当然是什么事都不做,接着对init方法中retain的对象release,最终someObj = nil。整个过程没有内存泄露,问题的关键还是在于setter覆盖造成的一些不那么明显的危险。


有兴趣的同学可以试着写出下面main.h的运行结果,加深理解。

#import <Foundation/Foundation.h>

@interface Father : NSObject

@property(retain)NSString *firstname;

@end

#import "Father.h"

@implementation Father

@synthesize firstname = _firstname;

-(id)init
{
    self = [super init];
    if (self) {
        [self setFirstname:@"Green"];
    }
    return self;
}

-(void)dealloc
{
    NSLog(@"Father:%@",NSStringFromSelector(_cmd));
    [self setFirstname:nil];
    [super dealloc];
}


@end

#import "Father.h"

@interface Son : Father

@property(retain)NSString *secondName;
@end

#import "Son.h"

@implementation Son

@synthesize secondName = _secondName;

-(id)init{
    self = [super init];
    if (self) {
        _secondName = @"Bload";
    }
    return self;
}

-(void)setFirstname:(NSString *)firstname
{
    NSLog(@"second name length is%ld",(unsigned long)[_secondName length]);
    [super setFirstname:firstname];
    NSLog(@"Son:%@",NSStringFromSelector(_cmd));
}


-(void)dealloc
{
    NSLog(@"Son:%@",NSStringFromSelector(_cmd));
    [_secondName release];
    //_secondName = nil;
    [super dealloc];
}
@end

#import <Cocoa/Cocoa.h>
#import "Son.h"
int main(int argc, char *argv[])
{
       
    Son *jack = [[Son alloc]init];
    NSLog(@"====================");
    NSLog(@"jack's first name is %@,second name is %@",jack.firstname,jack.secondName);
    NSLog(@"====================");
    [jack setFirstname:@"hil"];
    NSLog(@"====================");
    [jack release];
    return 0;
}








运行结果:

2014-02-24 23:45:56.074 test[1908:303] second name length is0
2014-02-24 23:45:56.115 test[1908:303] Son:setFirstname:
2014-02-24 23:45:56.117 test[1908:303] ====================
2014-02-24 23:45:56.118 test[1908:303] jack's first name is Green,second name is Bload
2014-02-24 23:45:56.159 test[1908:303] ====================
2014-02-24 23:45:56.159 test[1908:303] second name length is5
2014-02-24 23:45:56.163 test[1908:303] Son:setFirstname:
2014-02-24 23:45:56.164 test[1908:303] ====================
2014-02-24 23:45:56.165 test[1908:303] Son:dealloc
2014-02-24 23:45:56.179 test[1908:303] Father:dealloc
2014-02-24 23:45:56.180 test[1908:303] second name length is5 //设置nil时 为second name length is0
2014-02-24 23:45:56.182 test[1908:303] Son:setFirstname:


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值