在目标c中使用ivars vs属性的原因

时间:2012-07-13 20:39:13

标签: objective-c properties ivar

我一直无法找到有关此主题的任何信息,而且我所知道的大部分内容都是完全意外发生的(而且几个小时的时间试图弄清楚为什么我的代码不起作用)。在学习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的文档进行搜索,但我却迷失在那里,似乎从来没有找到我想要的东西。任何指导将不胜感激。

5 个答案:

答案 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)或无数其他原因的能力,您只需要 来合成它,在别处描述