具有相应ivars的属性的概念

时间:2012-03-24 19:02:38

标签: iphone objective-c ios properties

我对属性和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但未能得出最终结论。

4 个答案:

答案 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