我对属性和ivars有一般性问题。
我见过很多不同的例子来使用属性,这让我感到困惑。
方法1 仅使用没有相应ivar的属性。
@property (...) Type *name;
@synthesize name;
方法2 使用属性和ivar
@interface{
Type *ivarName;
}
@property (...) Type *name;
@synthesize name = ivarName;
方法3 忽略属性并使用ivars
@interface{
Type *ivarName;
}
ivar = ...;
我目前在大多数事情上使用方法1,它只是有效。但是我想知道我是否会在这里遗漏一些东西。我已经阅读了很多关于ivars VS属性的问题,但是他们似乎并没有真正关心它们如何协同工作。
在我见过的大多数示例项目中都使用了方法2。所以我的问题是:定义属性和ivar有什么好处,然后将属性分配给ivar,而不仅仅是拥有属性?
解决方案很简单:只有属性可以从'外部'设置ivar吗?
我读过:Must every ivar be a property?和Property vs. ivar in times of ARC但未能得出最终结论。
答案 0 :(得分:2)
当你写:
@synthesize name;
创建一个ivar名称,它与该属性具有相同的名称。所以你可以在有或没有自己的情况下访问它。
实际上如果你写
self.name = @"hello";
您正在访问该媒体资源,如果您正在编写
name = @"hello";
您正在访问ivar。大多数人(包括我)会建议您不要直接访问您的ivars,除非它真的是您想要的:例如,如果您要为该属性创建自定义setter或getter。否则总是自己进入酒店。
就我而言,我总是这样做:
@synthesize name = _name;
这种方法的优点是,当你忘记写自己而不是访问ivar时,你会收到一个错误,告诉你ivar名称不存在。
答案 1 :(得分:2)
您永远不应该直接从课外访问ivars。这是属性的主要功能 - 定义其他对象使用的访问器方法。但是,在同一个类中使用访问器也是一种好习惯 - 这样可以确保发生任何适当的副作用(如果您不使用ARC,内存管理就是一个明显的例子)。
因此,方法3通常是错误的。方法1大致相当于方法2 - 也就是说,在幕后,运行时基本上为您创建一个ivar。另请注意,即使您没有明确定义它,也可以设置该ivar的名称:
@interface{
//No ivar here!
}
@property (...) Type *name;
@synthesize name = ivarName;
答案 2 :(得分:2)
解决方案很简单:只有属性可以从'外部'设置ivar吗?
基本上,是的。 Obj-C中的Ivars(默认情况下)是“protected”,这意味着编译器不允许您在对象自己的代码外部访问它们。例如,给定以下类声明:
@interface Dunstable : NSObject
{
NSString * crunk;
}
@end
您可能认为在创建对象后可以访问ivar,但尝试导致错误:
Dunstable * d = [[Dunstable alloc] init];
d->crunk = @"Forsooth"; // Error: "Instance variable 'crunk' is protected
这就是ObjC使用访问器方法的原因。在声明的属性出现之前,必须手动定义它们:
@implementation Dunstable
- (NSString *)crunk {
return crunk; // implicit ivar access, i.e. self->crunk
}
- (void)setCrunk: (NSString *)newCrunk {
[newCrunk retain];
[crunk release];
crunk = newCrunk;
}
@end
现在,使用@property
和@synthesize
指令为您创建这些访问器方法(以及变量本身)。 (在ARC中,setter中的手动内存管理当然也已经过时了。)
可以制作一个可从对象外部访问的ivar:
@interface Dunstable : NSObject
{
@public
NSNumber * nonce;
}
@end
Dunstable * d = [[Dunstable alloc] init];
d->nonce = [NSNumber numberWithInt:2]; // Works fine
但这不被认为是好的Objective-C风格。
The Objective-C Programming Language doc包含关于此的“历史记录”:
注意:历史上,接口需要声明类的实例变量,这些数据结构是类的每个实例的一部分。这些在
@interface
声明之后和方法声明之前在括号中声明: [...] 实例变量表示实现细节,通常不应在类本身之外访问。此外,您可以在实现块中声明它们或使用声明的属性合成它们。因此,通常不应在公共接口中声明实例变量,因此应省略大括号。
这是一个非常大的变化(我真的感到惊讶的是,在该文档中已经没有为@interface
中声明的ivars提供语法),但它肯定会变得更好。你应该使用声明的属性;他们做正确的事情,让你的代码更清洁,更安全。
答案 3 :(得分:0)
从您提供的第二个链接Property vs. ivar in times of ARC开始,Denis Mikhaylov对已接受答案的评论非常明确。他指出,根据您的案例3,您可以通过以下方式访问iVar:
classInstance->iVar = @"New value"
但这被认为是不好的做法。所以我重申你的观点:
只有属性 才能从'外部'设置ivar