2009-08-18 79 views
6

今天早上我遇到了一个iPhone应用程序崩溃,当我修复这个bug时,我很好奇它是一个问题的句法原因。在不使用自引用的情况下设置Objective-C类属性

这里是我的代码简化为简单的元素。我使用NSArray为项目填充TableView中的项目。该NSArray的是一个属性:

@interface FooViewController : UITableViewController { 
    NSArray *stuff; 
} 

@property (nonatomic, retain) NSArray *stuff; 

在我的实现文件:

@synthesize stuff; 

- (void)viewDidLoad {  
    NSArray *arr = [[NSArray alloc] initWithObjects:@"", @"Item 1", @"Item 2", 
             @"Lorem", @"Ipsum", nil]; 
    self.stuff = arr; 

    [arr release]; 
} 

现在,当我第一次写的方法,我不小心不放过的“自我”。并导致了炸弹。虽然在测试时,它一开始就表现出色。我会尝试:用东西在轰炸其他方法

stuff = arr; 
NSLog(@"%d", [stuff count]); 

不过。现在我已经解决了这个问题,我可以在其他地方使用[stuff count]。

那么,为什么我可以在一些地方使用东西但在其他地方我必须使用self.stuff

回答

4

这还努力正确:

- (void)viewDidLoad {  
    stuff = [[NSArray alloc] initWithObjects:@"", @"Item 1", @"Item 2", 
             @"Lorem", @"Ipsum", nil]; 
} 

因为数组被ALLOC保留。但它通常是更好地坚持以点符号,如果你有一个属性,并使用自动释放阵列创建方法,你在哪里得到保留“免费”从属性:

- (void)viewDidLoad {  
    NSArray *arr = [NSArray arrayWithObjects:@"", @"Item 1", @"Item 2", 
             @"Lorem", @"Ipsum", nil]; 
    self.stuff = arr; 

} 

你可能只是离开它,以保持事情简单,但你也需要释放该数组中的dealloc:

- (void)dealloc {  
    [stuff release]; stuff = nil; 
} 
+1

如果您打算使用属性,那么您应该在任何地方使用它们,即dealloc内容应该简单地读取: self.stuff = nil; 帮助避免出错的常用方法是声明实例变量: NSArray * _stuff; 属性声明保持不变,但合成语句现在应该如下所示: @synthesize stuff = _stuff; 现在,每次尝试使用没有自己的东西时,编译器都会标记错误。 – 2010-02-08 09:41:37

+1

实际上Apple建议在dealloc中直接释放类变量,而不是使用属性 - 这是因为如果使用属性,可能会触发KVC通知,或者可能会通过覆盖合成的get/set方法启动其他副作用。 – 2010-02-08 20:18:05

+0

感谢您的提示。我想知道这是如何相关,特别是因为可能不想* KVO触发?更不用说在现代运行时中,我们将*拥有使用访问器和合成实例变量的机会。我想苹果正在确保他们的代码能够抵御这种情况。到目前为止,我还没有遇到任何与我的代码有关的问题,但是现在我已经警告过了:-) – 2010-02-21 23:50:15

3

stuff = ...直接引用属性的后台字段。它不会增加保留计数。因此,在其他地方释放对象可能会将其保留计数降至零,并在您仍然参照它时释放该对象。此外,它可能会导致内存泄漏的属性的以前的值。
它看起来像工作的原因有时是该对象可能还没有被其他人释放。

另一方面,self.stuff = ...将发送一条消息到属性的set访问器,该访问器将负责保留计数。

5

当你使用(self)和点语法时,给定你定义属性的方式(nonatomic,retain),保留NSArray(stuff)。

当你不这样做时,你仍然在做这个任务,但是你不是通过alloc + init保留数组,而是通过alloc + init保留数组,并且你马上释放它。

让您可以通过操作的方式从“self.stuff = ARR”分配:

stuff = [arr retain]; 

但既然你定义的属性,你显然要使用点语法和具有挽留你调用。

3

做的区别:

stuff=arr; 

self.stuff=arr; 

是在第二种情况下,您实际上正在调用自动​​合成的setStuff:accessor方法,该方法保留了该数组。在你发布的代码中,数组是使用alloc/initWithObjects创建的,因此它的保留计数为1.

你只需要改变在你的viewDidLoad方法中删除对[arr release]的调用,一切都会好起来的:

- (void)viewDidLoad {  
    NSArray *arr = [[NSArray alloc] initWithObjects:@"", @"Item 1", @"Item 2", 
             @"Lorem", @"Ipsum", nil]; 
    stuff = arr; 
} 

正如你注意到的,你也可以通过使用self.stuff来“修复”这个。我建议不要这样做,因为它掩盖了代码的含义,并添加了大多数情况下不需要的额外工作。一般来说,我建议不要使用“自我”。语法在您的实例方法中。

+0

因此,如果@property具有'assign'语义,那么在访问前不使用'self.'确实可以吗? – bobobobo 2012-09-08 21:03:47

+0

一般来说,是的。正如在其他答案中提到的那样,直接访问属性不会激发KVO观察员,(这可能是也可能不是理想的,具体取决于)。 – 2012-09-11 23:29:46

相关问题