关于cocoa:Objective-C中的属性和实例变量

Properties and Instance Variables in Objective-C

我对Objective-C中的属性和实例变量相当困惑。

我大概已经完成了Aaron Hillegas的"MacOSX的可可编程",一切都是合乎逻辑的。您将声明这样的类:

1
2
3
4
5
6
7
8
9
10
11
12
13
@class Something;

@interface MyClass : NSObject {
    NSString *name;
    NSArray *items;

    Something *something;

    IBOutlet NSTextField *myTextField;
}

@property (nonatomic, retain) NSString *name;
@property (nonatomic, retain) NSArray *items;
  • 由于其他对象需要操纵我们的nameitems实例变量,所以我们使用@property@synthesize为它们生成访问器/变异器。在我们的类中,我们不使用只与实例变量直接交互的访问器/转换器。

  • something只是我们将在类中使用的一个实例变量,由于没有其他人需要使用它,因此我们不会为它创建一对访问器和赋值器。

  • 我们需要与UI中的文本字段交互,因此我们为它声明一个IBOutlet,连接它,然后我们就完成了。

一切都很合乎逻辑。

然而,在iPhone世界里,情况似乎有所不同。人们为每个实例变量声明属性,为IBOutlets声明属性,并使用访问器/赋值器与类中的实例变量进行交互(例如,他们将写入[self setName:@"Test"],而不是name = @"Test")。

为什么?怎么回事?这些差异是iPhone特有的吗?为所有实例变量声明属性、为IBOutlets声明属性以及在自己的类中使用访问器/赋值函数有什么好处?


在iPhone世界里,没有垃圾收集器。你必须通过引用计数来小心地管理内存。考虑到这一点,考虑以下两者之间的区别:

1
name = @"Test";

1
2
3
self.name = @"Test";
// which is equivalent to:
[self setName: @"Test"];

如果直接设置实例变量,而不事先考虑,则会丢失对前一个值的引用,并且无法调整其保留计数(您应该手动使用released)。如果您通过属性访问它,它将自动为您处理,同时增加新分配对象的保留计数。

基本概念不是针对iPhone的,但在没有垃圾收集器的环境中,它变得至关重要。


属性用于为实例变量生成访问器,不会发生任何魔力。

您可以手动实现相同的访问器。

你可以在Aaron Hillegass的书中找到成员变量的3种记忆管理策略的例子。他们是assign/copy/retain。您可以根据给定变量的需要选择其中一个变量。

我假设你理解客观C中的记忆管理…

访问器隐藏每个变量的内存管理的复杂性和差异。

例如:

1
name = @"Test"

这是一个简单的任务,name现在引用NSString @"Test"。但是您可以决定使用copyretain。无论选择哪个版本的内存管理,访问器都隐藏了复杂性,并且始终使用(或类似的)访问变量:

1
2
[self setName:@"Test"]
[self name]

现在,setName:可能使用assign/copy or retain,你不必担心。

我的猜测是,iPhone教程使用属性使新开发人员更容易跳过内存管理(即使用属性生成适当的访问器,而不是每次都手工实现这些访问器也很方便)。


However, in the iPhone world, things seem to be different. People declare properties for every single instance variable, declare properties for IBOutlets, and use accessors/mutators to interact with instance variables within the class (e.g. they would write [self setName:@"Test"] rather than name = @"Test").

这不是iPhone特有的。除init方法和dealloc方法外,最好始终使用附件。主要的好处,特别是在Mac上(使用cocoa绑定),是使用访问器意味着免费的kvo通知。

人们"为每个实例变量声明属性"的原因很可能是他们的所有实例变量都是他们希望作为属性公开的东西。如果他们有一些想要保密的东西,他们不会在头文件中声明它的属性。(但是,他们可以在实现文件的类扩展名中为其创建一个属性,以便获得上述的免费KVO通知。)

在我看来,为销售点申报房产是多余的。我看不出有什么意义。如果不创建属性,NIB加载器将通过直接实例变量访问来设置出口,这对于该任务来说很好。


我建议,现代发展已经作出了非常强烈的尝试,以确定、定义和应用最佳实践。

在这些最佳实践中,我们发现了连续性和一致性。

除了争论在initdealloc方法中使用访问器之外,访问器通常应一直(在类内和类外)为它们提供的好处而使用,包括封装、多态var实现(既允许抽象和重构),并促进那些连续的最佳实践。一致性和一致性。当以这种方式做事情并充分利用语言能力时,面向对象语言的基本好处就会发挥出来。正如任何高级程序员通常会证明的那样,在编码中总是保持一致是一个经常被提到的好处。


你可以这样写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//MyClass.h

@class Something;

@interface MyClass : NSObject

@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSArray *items;

@end

//MyClass.m
@interface MyClass()

@property (nonatomic, strong) IBOutlet NSTextField *myTextField;
@property (nonatomic, strong) Something *something;

@end