好吧,这肯定是以前问过的,但我看起来很生气,什么也没发现:
我的iPhone应用程序中有一个简单的数组,我定义如下:
1 2
| @property (nonatomic, strong) NSArray *pages;
@synthesize pages = _pages; |
我在Apples示例代码中看到了这一点,并认为这是编写self.pages(即,使用页面替换self.pages)的一个很好的捷径,如下所示:
1
| _pages = [[NSArray alloc] init]; |
但苹果又有了这个(不完全是这样,但看起来好像他们一直在随机交换):
1
| self.pages = [NSKeyedUnarchiver unarchiveObjectWithData:contents]; |
最后:
这让我很困惑。页面和self.pages有什么区别?
谢谢你的帮助。
- 这确实是一个倍数:stackoverflow.com/q/3521254 stackoverflow.com/q/7174277 stackoverflow.com/q/5466496 stackoverflow.com/q/6049269 stackoverflow.com/q/822487 stackoverflow.com/q/2371489 stackoverflow.com/q/5582448等等。
_pages是对象的ivar的名称。pages是不同的属性名。因此,@synthesize pages = _pages;最后告诉我们,pages是ivar _pages的财产。
您将在初始值设定项和dealloc方法中通过_pages实现ivar直接访问。每隔一个地方,属性名都用于获取/设置其值。
- 说得好。请注意,Apple保留带有下划线前缀的名称,非Apple开发人员应避免使用下划线前缀,以避免名称冲突。如果要区分IVAR和属性名,可以使用下划线后缀:foo_而不是_foo。
- @卡莱布-说得对,也不错。谢谢您。
- @caleb——事实上,根据这篇文章,苹果只保留下划线用于方法名。
- @热舔,这是一个公平的观点。这是有意义的——ivar可以被设为私有的,如果您试图重新定义一个ivar,编译器应该发出警告。方法不是这样的。
- Hm.…我还是很困惑。开始思考这个问题:我可以从根本不使用页面开始吗?当我真的要用这个的时候?在我见过的所有示例应用程序中(初学者的课本中),它们从不使用ivar。我想他们有理由,不是吗?
- 有充分的理由是的。您可以避免使用_,也可以定义一个名为foo的ivar,并在其上定义一个名为bar的属性。我建议你也读这个。
- @我在我的帖子里很好地解释了这一区别,尽管卡莱布不喜欢。
这种差异来自于常见的命名和使用实践。
实例变量和属性都引用同一对象。命名差异用于指出ivar(_pages和属性(pages之间的差异。
ivar由类的实例拥有,由它们来处理它的所有权操作(alloc、retain、release等)。通常,这些所有权操作发生在init和dealloc中。
另一方面,该属性为IVAR提供"指定"访问点。属性方法(setter和getter)可以执行适当管理ivar所需的其他操作。因此,不建议直接访问ivar(作为一种使用模式),即使在拥有的对象中也是如此。例如,可以这样实现setter:
1 2 3 4 5 6 7 8 9
| - (void) setPages:(NSArray *)newValue {
[newValue retain];
// additional operations that you will miss if you use the ivar
[someObject someUsefulOperationThatIsReallyNeeded];
[pages release];
_pages = newValue;
} |
当您使用简单的分配时:
页=…
只需设置实例变量。
使用财产转让时:
self.pages=…
它调用由编译器自动合成的方法(或由您定义的方法),为了合成此方法,它检查属性的类型(retain、assign,…)并编写与此类型属性匹配的代码。
_pages是实例变量,pages是属性名。属性通过getter和setter方法pages和setPages:object.pages等同于[object pages]或例如`[self-setpages:[nskeedunarchiveobjectwitdata:contents]访问;
因此,唯一实际的对象是实例变量_pages,因此只有这个变量可以进行内存管理。
实际上,属性和合成器代码给出的代码与此代码相同(实际上,内存管理和线程锁定可能需要额外的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| @interface MyClass
{
...
NSArray *_pages
}
- (NSArray*)pages;
- (void)setPages:(NSArray*)newValue;
@end
@implementation MyClass
- (NSArray*)pages
{
return _pages;
}
- (void)setPages:(NSArray*)newValue
{
_pages = newValue; // Note in non ARC code there would be some memort managment here
}
@end |
您可以将@synthesized属性引用为instanceVariableName或self.propertyName。两个名称可以相同或不同。
当您引用为instanceVariableName并修改该值时,没有应用关联属性的保留/复制逻辑——您只是引用了"raw"变量。当您引用self.propertyName时,将应用保留/复制逻辑,例如,如果属性声明为"保留",则将释放旧值并保留新值。
将已保留的值(如alloc/init中的一个)分配给属性时,将该值分配给instanceVariableName并跳过对release的需要(这样,retains的净值在操作结束时将为1),会更简单(如果这是一个属性以前为nil的初始化)。但是,当将一个非retained的值(autoreleasedretain除外)分配给一个属性时,您希望该属性的retain发生,因此您将使用self.propertyName符号。
使用前导"uu"作为实例变量也是一个属性,这是保持这两个变量分开的简单约定,并且避免在您指另一个变量时意外引用一个变量(通过错误地添加/删除self)。
- 这在极端情况下是令人困惑的。您可以通过调用属性访问器方法来访问属性,或者通过执行调用它们的操作(例如使用点语法或键值编码)来访问属性。访问器方法定义属性。如果属性由实例变量支持,那么也可以直接访问该IVAR,但这与访问该属性不同。第3段是超越混乱和大不正确的:我们直接分配给初始值设定项中的ivar是为了安全,而不是为了简单。
- 不管你怎么描述,都很困惑。要知道的重要一点是,instanceVariableName和self.propertyName(对于@synthesized属性)在read时产生相同的值,但分配给它们会产生显著不同的语义。
- 不必那么混乱。访问属性,而不是ivar,初始化器和-dealloc中除外。
- @卡莱布——这回答不了问题。我赞成在-dealloc中使用self.propertyName = nil;。