Objective-c中的“实例变量”和“属性”之间有区别吗?

Is there a difference between an “instance variable” and a “property” in Objective-c?

目标C中的"实例变量"和"属性"之间有区别吗?

我对此不太确定。我认为"属性"是一个具有访问器方法的实例变量,但我可能认为是错误的。


属性是一个更抽象的概念。实例变量实际上只是一个存储槽,就像结构中的槽。通常其他对象不应该直接访问它们。另一方面,属性是可以访问的对象属性(听起来很模糊,应该是这样)。通常一个属性会返回或设置一个实例变量,但它可以使用多个或一个都不使用的数据。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@interface Person : NSObject {
    NSString *name;
}

    @property(copy) NSString *name;
    @property(copy) NSString *firstName;
    @property(copy) NSString *lastName;
@end

@implementation Person
    @synthesize name;

    - (NSString *)firstName {
        [[name componentsSeparatedByString:@""] objectAtIndex:0];
    }
    - (NSString *)lastName {
        [[name componentsSeparatedByString:@""] lastObject];
    }
    - (NSString *)setFirstName:(NSString *)newName {
        NSArray *nameArray = [name componentsSeparatedByString:@""];
        NSArray *newNameArray [[NSArray arrayWithObjects:newName, nil] arrayByAddingObjectsFromArray:[nameArray subarrayWithRange:NSMakeRange(1, [nameArray size]-1)]];
        self.name = [newNameArray componentsJoinedByString:@""];
    }
    - (NSString *)setLastName:(NSString *)newName {
        NSArray *nameArray = [name componentsSeparatedByString:@""];
        NSArray *newNameArray [[nameArray subarrayWithRange:NSMakeRange(0, [nameArray size]-2)] arrayByAddingObjectsFromArray:[NSArray arrayWithObjects:newName, nil]];
        self.name = [newNameArray componentsJoinedByString:@""];
    }
@end

(注:上述代码是错误的,因为它假定名称已经存在,并且至少有两个组件(例如,"bill gates"而不仅仅是"gates")。我觉得修改这些假设会使代码的实际意义不那么清晰,所以我只是在这里指出,所以没有人会无意中重复这些错误。)


属性是实现某个值的getter/setter的一种友好方法,它具有其他有用的特性和语法。属性可以由实例变量支持,但您也可以定义getter/setter来执行一些更动态的操作,例如,您可以在动态创建结果的字符串上定义小写属性,而不是返回某个成员变量的值。

下面是一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// === In your .h ===

@interface MyObject {
    NSString *propertyName;

}

// ...

@property (nonatomic, retain) NSString *propertyName;

// === In your .m @implementation ===

@synthesize propertyName /* = otherVarName */;

@property行定义了NSString *类型的名为propertyName的属性。可以使用以下语法获取/设置:

1
2
myObject.propertyName = @"Hello World!";
NSLog("Value: %@", myObject.propertyName);

当您分配或从myObject.propertyName中读取时,您实际上是在对象上调用setter/getter方法。

@synthesize行告诉编译器为您生成这些getter/setter,使用属性名称相同的成员变量来存储值(如果在注释中使用语法,则使用otherVarName)。

除了@synthesize之外,您还可以通过定义自己的getter/setter来覆盖其中一个getter/setter。这些方法的命名约定是setter的setPropertyName:,getter的propertyName(或getPropertyName,不是标准的)。另一个仍将为您生成。

在您的@property行中,您可以为属性定义许多属性,这些属性可以自动化线程安全和内存管理等操作。默认情况下,属性是原子的,这意味着编译器将使用适当的锁包装@synthesized get/set调用,以防止并发问题。您可以指定nonatomic属性来禁用该属性(例如,在iPhone上,您希望将大多数属性默认为nonatomic)。

有3个属性值控制任何@synthesizedsetter的内存管理。第一个是retain,它将自动将release发送到属性的旧值,并将retain发送到新值。这很有用。

第二个是copy,它将复制传入的任何值,而不是保留这些值。对nsstring使用copy是一个很好的实践,因为调用者可以传入一个nsmutablestring并将其从您的下面更改出来。copy将生成只有您可以访问的输入的新副本。

第三个是assign,它执行一个直接指针分配,而不在旧对象或新对象上调用retain/release。

最后,还可以使用readonly属性禁用属性的setter。


我使用接口部分的属性-其中对象与其他对象接口实例变量是您在类中需要的东西——除了您应该看到和操作它们之外,没有人需要它。


默认情况下,读写属性将由实例变量支持,编译器将再次自动合成该实例变量。

实例变量是一个存在的变量,在对象的生命周期中保存其值。用于实例变量的内存在对象首次创建时分配(通过alloc),在对象释放时释放。

除非您另外指定,否则合成实例变量与属性具有相同的名称,但带有下划线前缀。例如,对于名为firstname的属性,合成的实例变量将被称为_firstname。


以前人们公开使用财产,私人使用IVARs,但从几年前开始,您还可以在@implementation中定义私有财产。但如果可能的话,我还是会使用ivar,因为要输入的字母更少,而且根据本文,它运行得更快。这是有意义的,因为属性意味着"重":它们应该从生成的getter/setter或手动编写的getter/setter访问。

然而,在苹果公司最近的代码中,ivar不再使用。我想是因为它更像是objc,而不是C/C++,而且它更容易与assignnullable等一起使用属性。