声明的属性是否需要相应的实例变量?

103

Objective-C 2.0中的属性是否需要声明相应的实例变量?例如,我习惯这样做:

MyObject.h

@interface MyObject : NSObject {
NSString *name;
}
@property (nonatomic, retain) NSString *name;
@end

MyObject.m

@implementation
@synthesize name;
@end

然而,如果我改为这样做:

MyObject.h

@interface MyObject : NSObject {
}
@property (nonatomic, retain) NSString *name;
@end

这仍然有效吗?它与我的先前示例有何不同吗?


为什么第二个加粗的'MyObject.h'不是'MyObject.m'? - Ríomhaire
6个回答

93

如果您使用现代的Objective-C运行时(即iOS 3.x或更高版本,或64位Snow Leopard或更高版本),则在此类情况下,您不需要为属性定义ivars。

当您使用@synthesize属性时,变量将被自动生成。这可以解决“脆弱的实例变量”问题。您可以在Cocoa with Love中了解更多信息。


71
在接口中,你可以在花括号之间正式声明一个实例变量,或者通过 @property 在花括号之外声明,或者两者同时。无论哪种方式,它们都成为了类的属性。不同的是,如果你使用@property声明,那么你可以使用@synthesize来实现自动编写getter/setter方法。例如,自动编码的setter会将整数和浮点数初始化为零。如果你声明了一个实例变量,并且没有指定相应的@property,那么你就不能使用@synthesize,必须编写自己的getter/setter方法。
你总是可以通过指定自己的getter/setter方法来覆盖自动编写的getter/setter方法。这通常用于惰性加载的managedObjectContext属性。因此,你声明managedObjectContext作为一个属性,但是也要编写一个-(NSManagedObjectContext *)managedObjectContext方法。请注意,与实例变量/属性名称相同的方法被称为“getter”方法。 @property声明方法还允许你使用其他选项,如retainreadonly,而实例变量声明方法则不支持。基本上,ivar是旧的方法,而@property扩展了它并使其更加高级/易用。你可以使用self.前缀或者不使用,只要名称在该类中是唯一的就可以。否则,如果你的超类与属性具有相同的名称,则必须使用self.name或super.name来指定你所说的名称。因此,您会看到越来越少的人在花括号之间声明ivar,而是转向只指定@property,然后进行@synthesize。如果没有相应的@property,则无法在实现中使用@synthesize。合成器仅从@property规范中了解它是什么类型的属性。合成语句还允许您重命名属性,以便您可以在代码内部使用一个名称(缩写),但在.h文件中外部使用完整名称。但是,随着XCode现在具有非常酷的自动完成功能,这不再是优势,但仍然存在。
希望这能帮助澄清所有混乱和误传。

现在不强制使用 @synthesize 了。那么在这种情况下,这个答案怎么有效呢? - raaz
你不必须声明 <code>@property...@synthesize</code>。使用 synthesized 会减轻你在实现中编写 getter/setter 的负担。如果你没有使用 synthesized,那么你必须自己编写 getter/setter。 - PapaSmurf
2
@PapaSmurf 这是不正确的。您可以使用 @property,而不必使用 @synthesize 并且不必自己实现它们。编译器会自动为您合成,无需再编写。 - jbrennan

8

这个方法可以双向工作,但是如果你不在花括号中声明它们,你将看不到它们在Xcode调试器中的值。


3
如果您使用的是XCode 4.4或更高版本,则会为您生成实例变量合成代码。 您只需要声明以下属性;它将为您生成合成代码和实例变量声明代码。
@property (nonatomic, strong) NSString *name;

它将生成合成代码,如下

@synthesize name = _name;

你可以使用_name来访问实例变量,这类似于声明。

NSString* _name

但是,如果您像这样声明只读属性
@property (nonatomic, strong, readonly) NSString *name;

它将生成代码。
@synthesize name;

或者
@synthesize name = name; 

因此,您应该不使用前缀“_”来访问即时变量名称。无论如何,您都可以编写自己的合成代码,然后编译器将为您生成代码。您可以编写

@synthesize name = _name;

3

从文档中可以得知:

一般来说,现代运行时和旧版运行时的属性行为是相同的(请参见“Objective-C Runtime Programming Guide”中的“Runtime Versions and Platforms”)。但是有一个关键区别:现代运行时支持实例变量综合,而旧版运行时则不支持。

在旧版运行时中,要使 @synthesize 起作用,您必须提供与属性名称和兼容类型相同的实例变量或在 @synthesize 语句中指定另一个现有的实例变量。对于现代运行时,如果您没有提供实例变量,则编译器会为您添加一个。


1

Objective-C编程语言:属性实现指令

访问器综合行为存在运行时差异(另请参见“运行时差异”):

  • 对于旧版运行时,实例变量必须已在当前类的@interface块中声明。如果同名实例变量存在且其类型与属性类型兼容,则使用它;否则,会出现编译器错误。

  • 对于现代运行时(请参阅Objective-C运行时编程指南中的“运行时版本和平台”),将根据需要综合实例变量。如果同名实例变量已经存在,则使用它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接