我一直无法找到有关此主题的任何信息,而且我所知道的大部分内容都是完全意外发生的(而且几个小时的时间试图弄清楚为什么我的代码不起作用)。在学习objective-c的大多数教程时,我发现使用相同的名称创建变量和属性。我不明白其重要性,因为看起来该物业完成了所有的工作,变量只是坐在那里。例如:
Test.h
@interface Test : NSObject {
int _timesPlayed, _highscore;
}
@property int timesPlayed, highscore;
// Methods and stuff
@end
Test.m
@implementation Test
@synthesize timesPlayed = _timesPlayed;
@synthesize highscore = _highscore;
// methods and stuff
@end
我所知道的
1)好的,今天我发现(经过几个小时的混乱),无论你对属性highscore = 5091231
做了多少改变,当你试图调用[test highscore]时它都不会改变任何东西因为它仍将返回_highscore的值,我认为它是在test.h中设置的ivar。因此,test.m中所有变量的变化都需要改变_highscore而不是highscore。 (如果我在这里错了,请纠正我)
2)如果我理解正确(我可能不会),test.h中设置的ivars代表实际内存,因为@properties只是访问该内存的方式。因此,在实施之外,我无法通过财产访问_highscore。
我不明白
基本上我不了解这种情况的方法是我是否需要使用ivars,或者我是否可以使用@property和@synthesize。似乎ivars只是额外的代码,除了让我困惑之外什么都不做。我见过的一些最新的东西似乎并没有使用伊娃,但有些人却这么做。那么这只是编码偏好的事情还是实际上很重要?我试过通过Apple的文档进行搜索,但我却迷失在那里,似乎从来没有找到我想要的东西。任何指导将不胜感激。
答案 0 :(得分:35)
您可以将合成属性的语法视为@synthesize propertyName = variableName
。
这意味着如果您编写@synthesize highscore = _highscore;
,将为您创建名为_highscore
的新ivar。因此,如果您希望通过转到_highscore
变量,可以直接访问属性存储的变量。
在某些版本的编译器之前我不记得合成语句没有创建ivar。相反,它只说它应该使用什么变量,所以你必须声明变量和属性。如果使用下划线前缀合成,那么您的变量需要具有相同的前缀。现在您不必再自己创建变量了,而是创建了一个在合成语句中指定的variableName
变量(如果您自己没有声明它,在这种情况下它只是使用它)作为财产的支持变量。)
在声明变量时显式创建一个名为highscore
的ivar,然后在合成属性时隐式创建另一个名为_highscore
的ivar。这些变量不是同一个变量,因此更改其中一个变量不会改变另一个变量。
这是一个关于偏好的问题。
如果您不必在整个地方写self.
,有些人会觉得代码会变得更干净。人们还说它更快,因为它不需要方法调用(尽管它可能永远不会对你的应用程序性能产生可测量的影响)。
更改属性的值将调用所有必需的KVO方法,以便在值更改时通知其他类。默认情况下,对属性的访问也是原子的(不能从多个线程访问),因此从多个线程读取和写入属性更安全(这并不意味着属性指向的对象是线程安全的,如果它是一个可变数组然后多个线程仍然可以破坏非常糟糕的东西,它只会阻止两个线程将属性设置为不同的东西。)
答案 1 :(得分:3)
根据您的建议,您可以使用@property
和@synthesize
而无需声明ivars。上面的问题是您的@synthesize
将属性名称映射到由编译器生成的新ivar。因此,对于所有意图和目的,您的类定义现在是:
@interface Test : NSObject {
int timesPlayed;
int highscore;
int _timesPlayed;
int _highscore;
}
...
@end
如果您通过timesPlayed
访问,则永远不会显示为ivar self.timesPlayed
直接分配值,因为您没有修改正确的ivar。
您有多种选择:
1删除您在原始帖子中声明的两个ivars,然后让@property
/ @synthesize
动态二人组完成他们的工作。
2将两个ivars更改为以下划线'_'
作为前缀 3将您的@synthesize
语句更改为:
@implemenation Test
@synthesize timesPlayed;
@synthesize highscore;
...
答案 2 :(得分:2)
我通常只使用@property和@synthenize。
@property为编译器和用户提供有关如何使用属性的说明。天气它有一个二传手,那个二传手是什么。它期望和返回什么类型的价值。然后,自动完成(最终将针对类编译的代码)和@synthesize
使用这些指令。@synthesize默认情况下会创建一个与您的属性同名的实例变量(这会让人感到困惑)
我通常会执行以下操作
@synthesize propertyItem = _propertyItem;
这将默认创建一个getter和一个setter并处理自动释放以及创建实例变量。它使用的实例变量是_propertyItem。如果要访问实例变量,可以使用它。
_propertyItem = @"Blah";
这是一个错误。你应该总是使用getter和setter。这将允许应用程序根据需要发布和续订。
self.propertyItem = @"Blah";
这是处理它的更好方法。使用合成的= _propertyItem部分的原因是你无法做到以下几点。
propertyItem = @"Blah"; // this will not work.
它会建议你用_propertyItem替换它。但你应该使用self.propertyItem。
我希望这些信息有所帮助。
答案 3 :(得分:0)
在您的示例中,@synthesize timesPlayed = _timesPlayed;
创建了一个名为_timesPlayed
的新ivar,该属性引用了该ivar。 timesPlayed
将是一个完全独立的变量,与财产无关。如果您只使用@synthesize timesPlayed;
,则该属性会引用timesPlayed
。
下划线约定的目的是当你想要通过属性(即通过合成的setter方法)时,更容易避免意外地直接分配给ivar。但是,如果你真的想要,你仍然可以直接访问_timesPlayed。合成属性只需为ivar自动生成一个getter和setter。
一般情况下,您不需要为属性声明ivar,尽管可能存在您想要的特殊情况。
答案 4 :(得分:-2)
这可能是一个老问题..但在“现代”中,@synthesize
- 不是必需的。
@interface SomeClass : NSObject
@property NSString * autoIvar;
@end
@implementation SomeClass
- (id) init { return self = super.init ? _autoIvar = @"YAY!", self : nil; }
@end
_underscored
支持ivar自动合成...并且可以在此类实现中直接使用(即,无需调用self
/调用自动生成的访问器)。
如果您想支持子类访问_backingIvar
(不调用self
)或无数其他原因的能力,您只需要 来合成它,在别处描述。