使用访问器方法设置iVars?

时间:2010-02-11 15:55:51

标签: objective-c cocoa-touch

最初,我正在研究“pickerData”的设置方式,并且我想知道为什么你不能直接分配它(如METHOD_002),但后来我说我应该真的使用访问器方法我定义并不直接设置实例变量。我是否正确理解METHOD_001是一种更好的方法?

@property(nonatomic, retain) IBOutlet NSArray *pickerData;

METHOD_001

-(void)viewDidLoad {
    NSLog(@"VIEW: Single ... Loaded");
    NSArray *dataArray = [[NSArray alloc] initWithObjects:@"A", @"B", @"C",nil];
    [self setPickerData:dataArray];
    [dataArray release];
    [super viewDidLoad];
}

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

OR METHOD_002

-(void)viewDidLoad {
    NSLog(@"VIEW: Single ... Loaded");
    if(pickerData != nil) [pickerData release];
    pickerData = [[[NSArray alloc] initWithObjects:@"A", @"B", @"C", nil] retain];
    [super viewDidLoad];
}

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

EDIT_001:

首先我添加了“nil”值来终止NSArrays,来自C我总是忘记这一点,我的不好。你也是对的,我没有考虑METHOD_002中的事实,即pickerData可能已经被设置,结果泄漏了旧对象。一旦你开始注意到这些问题并修复代码,它开始看起来像METHOD_001是最好的主意。或者直接使用该物业,如Vladimir和eJames所说。

self.pickerData = [NSArray arrayWithObjects: @"A", @"B", @"C", nil];

EDIT_002:

感谢所有指针和评论,现在我将坚持使用METHOD_001,我可以很容易地使用NSArrayWithObjects:但我试图通过尽快自己发布内容来保持内存使用率低(不是它在这里很重要,但对于未来的项目)我也喜欢self.pickerData的感觉,但我仍然不确定我对点符号的感觉,并且现在一直试图坚持使用旧式对象和消息。再次感谢你的帮助。

加里

3 个答案:

答案 0 :(得分:3)

这里有很多主题要讨论,但我会从推荐的方法开始:

METHOD_003

-(void)viewDidLoad { 
    NSLog(@"VIEW: Single ... Loaded"); 
    self.pickerData = [NSArray arrayWithObjects:@"A", @"B", @"C", nil]; 
    [super viewDidLoad];
}

注意:感谢弗拉基米尔为reminder to nil-terminate

现在,详情如下:

METHOD_001与METHOD_003完全相同,但需要多行代码。

METHOD_002因为alloc数组而导致内存泄漏,然后retain。这导致总保留计数为2.当您在dealloc方法中释放数组时,计数将减少为1,因此数组将不会从内存中释放。

即使您从METHOD_002中移除了额外的retain,它也不会做两件非常重要的事情:

  1. 它不会发送正确的KVC/KVO notifications。当你使用属性访问器时,Cocoa在幕后做很多非常方便的事情,所以除非你有充分的理由不这样做,强烈建议使用访问器。

  2. 它不会自动释放pickerData中存储的任何旧数据。这不是viewDidLoad中的问题,但如果你使用的方法不同,有所作为,所以最好养成使用访问者的习惯,除非你有特别的理由不这样做。

答案 1 :(得分:3)

您应始终使用属性的访问者(在Objective-C 2.0中表示使用self.property表示法。)

为什么呢?因为它提供自动访问控制和对象生命周期管理。生成的访问器可以提供很多保护,例如读/写,复制,保留等,否则会占用大量的手动代码。如果您编写自己的访问者,则可以添加所需的所有验证和副作用。

(回到Objective-C 2.0之前,写入访问器被认为是一门艺术。如果你充分发挥潜力,它仍然可以。)

您应该直接访问属性的唯一时间是编写访问者。例如,采用这种常见模式:

@property(nonatomic, retain)  NSMutableArray *myObjects;
@synthesize myObjects;

-(NSMutableArray *) myObjects{
    if (myObect!=nil) {
        return myObect;
    }
    NSMutableArray *anArray=[[NSMutableArray alloc] initWithCapacity:1];
    self.myObject=anArray;
    [anArray release]
    return myObject;
}
  1. 此访问器确保myObjects永远不会为nil,这会在代码的其余部分中删除大量的样板nil测试。
  2. 你显然不能在访问者内部调用self.myObjects(实际上是[self myObjects])而不创建无限递归,所以你必须在这里访问原始变量但是......
  3. ...你可以调用(自动生成的)self.myObjects=(实际上是[self setMyObjects:anArray])因为它是一个完全不同的方法。如果你看一下setMyObjects的内部结构:你会看到它也访问了原始变量。
  4. 如果您使用生成的访问者,self.myObjects=会在您每次调用时为您处理保留,复制,禁止等操作。您必须致电释放的唯一时间是dealloc。仅这一点就可以消除人们在Objective-C中犯下的错误的一半。
  5. 相反,在访问器方法之外,通过直接访问类自己的方法中的属性,您绝对不会获得任何结果。它所做的就是保存一些关键笔划,同时让您面临难以发现错误的风险。

    如前面的答案所示,您通过尝试直接管理属性而犯了几个内存错误。如果你每次都使用了存取器,你就不会制造它们。例如:

    pickerData = [[[NSArray alloc] initWithObjects:@"A", @"B", @"C", nil] retain];
    

    ......每次都必须完全正确地管理......

    self.pickerData = [[NSArray alloc] initWithObjects:@"A", @"B", @"C", nil];
    

    ...自动纠正。

    请记住,任何Objective-C类的最终设计目标都应该是完全模块化和可重用的。这意味着它应该管理自己的所有内存,自己的数据验证和自己的副作用。访问者对于管理来说绝对至关重要。通过围绕变量的每次访问包装逻辑,您可以确保(1)它是您期望的类型,范围等,以及(2)当您需要时它始终存在(3)您可以控制所有方面写入或读取变量的效果和(4)它不会泄漏。

    我不能颂扬访问者的优点。事实上,我可能会写一首小歌。 ; - )

答案 2 :(得分:0)

您的METHOD_002的最大问题是:a)您保留了一个您已经拥有的阵列,您不需要这样做(并且会泄漏阵列),以及b)您没有考虑到这种可能性pickerData可能已经有一个非nil值(viewDidLoad可能会在控制器的整个生命周期内多次调用。)

作为一种风格问题,第一种方法可能更好。它本来可以避免(b),并且通常处理很多细节,所以你不必这样做。