在不使用自引用的情况下设置Objective-C类属性 在我修好了程序错误的时候,我在一个iPhone应用程序中遇到了崩溃,我对语法原因感到好奇。

在不使用自引用的情况下设置Objective-C类属性 在我修好了程序错误的时候,我在一个iPhone应用程序中遇到了崩溃,我对语法原因感到好奇。,objective-c,iphone-sdk-3.0,Objective C,Iphone Sdk 3.0,下面是我的代码简化为简单元素。我正在使用项目的NSArray填充TableView中的项目。NSArray是一个财产: @interface FooViewController : UITableViewController { NSArray *stuff; } @property (nonatomic, retain) NSArray *stuff; - (void)viewDidLoad { NSArray *arr = [NSArray arrayWithO

下面是我的代码简化为简单元素。我正在使用项目的NSArray填充TableView中的项目。NSArray是一个财产:

@interface FooViewController : UITableViewController {
    NSArray *stuff;
}

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

}
在我的实现文件中:

@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]


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

stuff=…
直接引用属性的backing字段。它不会增加保留计数。因此,在其他地方释放该对象可能会使其保留计数降至零,并在您仍保留对该对象的引用时将其释放。此外,它可能会导致属性的上一个值出现内存泄漏。
它看起来有时能工作的原因是对象可能还没有被其他人释放

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

使用(self)和点语法时,根据定义属性的方式(非原子,保留),NSArray(stuff)将被保留

如果不这样做,您仍然在进行赋值,但是除了通过alloc+init隐式地保留数组之外,您没有保留数组-并且您立即将其释放

您可以通过以下操作通过“self.stuff=arr”绕过分配:

stuff = [arr retain];

但是由于您定义了一个属性,您显然希望使用点语法并调用retain。

这也可以正常工作:

- (void)viewDidLoad {     
    stuff = [[NSArray alloc] initWithObjects:@"", @"Item 1", @"Item 2",   
                                       @"Lorem", @"Ipsum", nil];
}
因为数组由alloc保留。但是,如果您有一个属性并使用autorelease数组创建方法,则通常最好使用点表示法,从属性中“免费”获得保留:

@interface FooViewController : UITableViewController {
    NSArray *stuff;
}

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

}
您可能只是为了保持简单而忽略了它,但您还需要在dealloc中释放此数组:

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

做以下事情的区别:

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来“修复”这个问题。我建议不要这样做,因为它模糊了代码的含义,并增加了在大多数情况下不需要的额外工作。一般来说,我建议不要在实例方法中使用“self.”语法。

如果要使用属性,那么应该在任何地方都使用它们,即dealoc的内容应该是:self.stuff=nil;帮助避免出错的常见方法是将实例变量声明为:NSArray*\u stuff;属性声明保持不变,但synthesis语句现在应该如下所示:@synthesis stuff=\u stuff;现在,每当你尝试在没有self的情况下使用stuff时,编译器都会标记一个错误。实际上,Apple在dealloc中建议直接释放类变量,而不是使用属性-这是因为如果你使用属性,可能会触发KVC通知,或者可能会因为覆盖合成的get/set方法而产生其他副作用。谢谢你的提示。我想知道这有多重要,尤其是因为人们可能不希望KVO触发?更不用说,在现代运行时,我们将不得不使用带有合成实例变量的访问器。我想苹果公司正在确保他们的代码能够防止类似的事情。到目前为止,我的代码还没有遇到任何问题,但我会注意的,现在有人警告我:-)不希望KVO触发是一个合理的问题,苹果说直接在init中设置值,并直接在dealloc中释放它们就是因为这个原因。因为这是在viewDidLoad中,我认为它可能更可取。。。虽然我可以看出我想把它当作一个开始。如果这种方法应该随着现代运行时的变化而改变,我将不得不自己考虑更多……因此,如果@property具有
assign
语义,那么在access前面不使用
self.
就可以了?一般来说,可以。正如其他答案中提到的,直接访问属性也不会触发KVO观察器(这可能是可取的,也可能是不可取的,具体取决于)。