【发布时间】:2010-10-25 00:35:31
【问题描述】:
Objective-c 中的“实例变量”和“属性”有区别吗?
对此我不太确定。我认为“属性”是具有访问器方法的实例变量,但我可能想错了。
【问题讨论】:
标签: objective-c
Objective-c 中的“实例变量”和“属性”有区别吗?
对此我不太确定。我认为“属性”是具有访问器方法的实例变量,但我可能想错了。
【问题讨论】:
标签: objective-c
属性是一个更抽象的概念。实例变量实际上只是一个存储槽,就像结构中的一个槽。通常其他对象不应该直接访问它们。另一方面,属性是可以访问的对象的属性(听起来很模糊,但应该如此)。通常一个属性会返回或设置一个实例变量,但它可以使用来自多个或根本不使用的数据。例如:
@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
(注意:上面的代码是错误的,因为它假设名称已经存在并且至少有两个组成部分(例如“比尔盖茨”而不仅仅是“盖茨”)。我觉得修正这些假设会成为实际的观点代码不太清楚,所以我只是在这里指出,所以没有人会无辜地重复这些错误。)
【讨论】:
属性是为某个值实现 getter/setter 的一种友好方式,具有其他有用的特性和语法。属性可以由实例变量支持,但您也可以定义 getter/setter 来做一些更动态的事情,例如您可以在字符串上定义一个 lowerCase 属性,该属性动态创建结果,而不是返回某个成员变量的值。
这是一个例子:
// === In your .h ===
@interface MyObject {
NSString *propertyName;
}
// ...
@property (nonatomic, retain) NSString *propertyName;
// === In your .m @implementation ===
@synthesize propertyName /* = otherVarName */;
@property 行定义了一个名为propertyName 的属性,类型为NSString *。这可以使用以下语法获取/设置:
myObject.propertyName = @"Hello World!";
NSLog("Value: %@", myObject.propertyName);
当您分配给myObject.propertyName 或从myObject.propertyName 读取时,您实际上是在调用对象的setter/getter 方法。
@synthesize 行告诉编译器为您生成这些 getter/setter,使用与属性同名的成员变量来存储值(或 otherVarName,如果您使用 cmets 中的语法)。
与@synthesize 一起,您仍然可以通过定义自己的方法来覆盖其中一个getter/setter。这些方法的命名约定是setPropertyName: 用于setter,propertyName(或getPropertyName,非标准)用于getter。另一个仍会为您生成。
在@property 行中,您可以在括号中为属性定义许多属性,这些属性可以自动执行线程安全和内存管理等操作。默认情况下,属性是原子的,这意味着编译器将使用适当的锁包装@synthesized get/set 调用以防止并发问题。您可以指定 nonatomic 属性来禁用此功能(例如,在 iPhone 上您希望将大多数属性默认为 nonatomic)。
有 3 个属性值控制任何 @synthesized 设置器的内存管理。第一个是retain,它将自动将release 发送到属性的旧值,并将retain 发送到新值。这非常有用。
第二个是copy,它将复制传入的任何值,而不是保留它们。对 NSString 使用 copy 是一种很好的做法,因为调用者可以传入 NSMutableString 并将其从您的下方更改。 copy 将制作一个只有您可以访问的输入的新副本。
第三个是assign,它直接分配指针而不对旧对象或新对象调用retain/release。
最后,您还可以使用readonly 属性来禁用该属性的设置器。
【讨论】:
我使用接口部分的属性 - 对象与其他对象的接口 和实例变量是你在类中需要的东西——除了你之外没有人应该看到和操纵它们。
【讨论】:
默认情况下,读写属性将由实例变量支持,该变量将再次由编译器自动合成。
实例变量是一个存在并在对象生命周期内保持其值的变量。用于实例变量的内存在对象首次创建时分配(通过 alloc),并在对象解除分配时释放。
除非您另外指定,否则合成的实例变量与属性具有相同的名称,但带有下划线前缀。例如,对于名为 firstName 的属性,合成的实例变量将称为 _firstName。
【讨论】:
以前人们使用公开的属性和私人使用的 ivars,但从几年前开始,您还可以在 @implementation 中定义属性以私下使用它们。但我仍然会尽可能使用 ivars,因为要输入的字母更少,而且根据this article,它运行得更快。这是有道理的,因为属性意味着“重”:应该从生成的 getter/setter 或手动编写的 getter/setter 访问它们。
但是,在 Apple 最近的代码中,不再使用 ivars。我猜是因为它更像objc 而不是C/C++,而且使用assign、nullable 等属性更容易。
【讨论】:
@implementation 中的属性是想显示与 Swift 的相似之处。我仍然更喜欢支持变量而不是浪费虚拟函数调用,以便查找我自己的类的简单字段(访问属性时会发生这种情况)。
Objective-C 属性与实例变量 (iVar)
实例变量
@interface SomeClass: NSObject
NSString *someVariable;
@end
房产
@interface SomeClass: NSObject
@property (nonatomic, strong) NSString *someVariable;
@end
Property 在内部使用 Instance variable。 property = variable + bounded getter/setter。它是一个具有可变语法和访问权限的方法调用
@property 生成 getter 和 setter methods(访问器方法),它们使用 backing ivar(又名支持字段),您可以通过下划线使用 _<var_name> (_someVariable) .
因为它调用一个方法 - 使用了method dispatch 机制,这就是为什么可以应用KVO[About]
当您覆盖访问器方法时,不会生成支持 iVar,这就是为什么您可以显式声明新属性或使用 @synthesize[About] 生成新属性或与现有属性链接的原因
#import "SomeClass.h"
@interface SomeClass()
@property (nonatomic, strong) NSString *someVariable;
@end
@implementation SomeClass
- (void) foo {
//property getter method
NSString *a1 = self.someVariable; //NSString *a1 = [self someVariable];
//property setter method
self.someVariable = @"set someVariable"; //[self setSomeVariable:@"set someVariable"];
//iVar read
NSString *a2 = _someVariable;
//iVar write
_someVariable = @"set iVar";
}
//if you overriding someVariable getter and setter the iVar(_someVariable) is not generated, that is why you can:
//1. create some variable explicitly
NSString *_someVariable;
//or
//2. use @synthesize
@synthesize someVariable = _someVariable;
//overriding
- (NSString*) someVariable {
return _someVariable;
}
- (void)setSomeVariable: (NSString*) updatedSomeVariable {
_someVariable = updatedSomeVariable;
}
@end
【讨论】: