24

我一直找不到关于这个主题的任何信息,而且我所知道的大部分信息都是完全偶然的(并且花了几个小时试图弄清楚我的代码为什么不起作用)。在学习objective-c时,我发现大多数教程都使用相同的名称制作变量和属性。我不明白其重要性,因为似乎该属性完成了所有工作,而变量只是坐在那里。例如:

测试.h

@interface Test : NSObject {
    int _timesPlayed, _highscore;
}

@property int timesPlayed, highscore;

// Methods and stuff

@end

测试.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 只是额外的代码,除了让我感到困惑之外,它并没有真正做任何事情。我见过的一些最近的 tuts 似乎没有使用 ivars,但后来有些使用了。那么这只是编码偏好还是实际上很重要?我曾尝试通过 Apple 的文档进行搜索,但我在其中迷失了方向,似乎从未找到我想要的东西。任何指导将不胜感激。

4

5 回答 5

38

您可以将合成属性的语法视为@synthesize propertyName = variableName.

这意味着如果您编写@synthesize highscore = _highscore;一个具有该名称的新 ivar,_highscore将为您创建。因此,如果您愿意,您可以通过访问变量直接访问存储属性的_highscore变量。

一些背景

在某些我不记得的编译器版本之前,综合语句没有创建 ivar。相反,它只说明了它应该使用什么变量,所以你必须声明变量和属性。如果您使用下划线前缀合成,那么您的变量需要具有相同的前缀。现在您不必再自己创建变量,而是将创建一个具有variableName您在综合语句中指定的变量(如果您还没有自己声明它,在这种情况下它只是用作财产)。

你的代码在做什么

highscore在声明变量时显式创建一个调用的 ivar,然后_highscore在合成属性时隐式创建另一个调用的 ivar。这些不是同一个变量,因此更改其中一个不会改变另一个。

是否应该使用变量?

这确实是一个关于偏好的问题。

临变量

有些人觉得如果你不必到处写代码,代码就会变得更干净self.。人们还说它更快,因为它不需要方法调用(尽管它可能永远不会对您的应用程序性能产生可衡量的影响)。

专业属性

更改属性的值将调用所有必要的 KVO 方法,以便其他类可以在值更改时得到通知。默认情况下,对属性的访问也是原子的(不能从多个线程访问),因此从多个线程读取和写入属性更安全(这并不意味着属性指向的对象是线程安全的,如果它是一个可变数组,然后多线程仍然可以破坏非常糟糕的东西,它只会阻止两个线程将属性设置为不同的东西)。

于 2012-07-13T20:51:09.530 回答
5

正如您所建议的,您可以只使用@property@synthesize不声明 ivars。上面的问题是您@synthesize将属性名称映射到编译器生成的新 ivar。因此,出于所有意图和目的,您的类定义现在是:

@interface Test : NSObject {
int timesPlayed;
int highscore;
int _timesPlayed;
int _highscore;
}
...
@end

如果您通过访问它,直接为 ivar 分配值timesPlayed将永远不会显示,self.timesPlayed因为您没有修改正确的 ivar。

你有几个选择:

1 删除您在原始帖子中声明的两个 ivars,让@property/@synthesize动态二人组做他们的事情。

2 将您的两个 ivars 更改为以下划线“_”为前缀

3 将您的@synthesize陈述更改为:

@implemenation Test
@synthesize timesPlayed;
@synthesize highscore;

...
于 2012-07-13T20:50:32.527 回答
2

我通常只使用@property 和@synthenize。

@property 为编译器和用户提供有关如何使用您的属性的说明。天气它有一个二传手,那个二传手是什么。它期望并返回什么类型的值。这些指令然后被自动完成(以及最终将针对类编译的代码)和@synthesize 使用

@synthesize 默认会创建一个与你的属性同名的实例变量(这可能会让人感到困惑)

我通常会执行以下操作

@synthesize propertyItem = _propertyItem; 

这将默认创建一个 getter 和一个 setter 并处理自动释放以及创建实例变量。它使用的实例变量是_propertyItem。如果你想访问实例变量,你可以这样使用它。

_propertyItem = @"Blah";

这是一个错误。您应该始终使用 getter 和 setter。这将使应用程序根据需要发布和更新。

self.propertyItem = @"Blah";

这是处理它的更好方法。使用 synthesize 的 = _propertyItem 部分的原因是您不能执行以下操作。

propertyItem = @"Blah"; // this will not work.

它会建议您将其替换为 _propertyItem。但您应该改用 self.propertyItem 。

我希望这些信息有所帮助。

于 2012-07-13T20:56:46.753 回答
0

在您的示例中,@synthesize timesPlayed = _timesPlayed;创建一个名为的新 ivar _timesPlayed,并且该属性引用该 ivar。 timesPlayed将是一个完全独立的变量,与属性没有任何关系。如果您只是使用@synthesize timesPlayed;,那么该属性将引用timesPlayed.

下划线约定的目的是更容易避免当您想通过属性(即通过合成的 setter 方法)直接分配给 ivar 时意外地直接分配给 ivar。但是,如果您真的想要,您仍然可以直接访问 _timesPlayed。合成一个属性只是为 ivar 自动生成一个 getter 和 setter。

通常,您不需要为属性声明 ivar,尽管可能存在您想要的特殊情况。

于 2012-07-13T20:50:08.790 回答
0

这可能是一个老问题..但在“现代”,@synthesize- 是不必要的。

@interface       SomeClass : NSObject
@property NSString * autoIvar;
@end
@implementation  SomeClass
- (id) init { return self = super.init ? _autoIvar = @"YAY!", self : nil; }
@end

_underscored支持 ivar 是自动合成的......并且可以在 THIS 类的实现中直接使用(即,无需调用self/调用自动生成的访问器)。

如果您想支持子类访问(无需调用)的能力,或者出于其他原因描述的无数其他原因,您只需要合成它。_backingIvarself

于 2014-02-14T06:49:31.870 回答