2012-03-24 70 views
2

我有一个关于属性和ivars的一般问题。与相应的ivars属性的概念

我看到许多不同的例子使用属性,它让我困惑了一下。

方法1只使用一个没有相应ivar的财产。

@property (...) Type *name; 

@synthesize name; 

方法2使用属性和实例变量

@interface{ 
Type *ivarName; 
} 
@property (...) Type *name; 

@synthesize name = ivarName; 

方法3级忽略的属性和与实例变量

@interface{ 
Type *ivarName; 
} 
ivar = ...; 

我目前使用方法1对于大多数事情工作的i-做,它只是工作。但我已经开始怀疑我是否可能在这里错过了一些东西。我已经阅读了很多关于ivars VS属性的问题,但他们都没有真正关心他们如何一起工作。

在我见过的大多数示例项目中使用了方法2。所以我的问题是:定义一个财产和伊娃有没有优势,然后将财产分配给伊娃,而不仅仅是一个财产?

是一个简单的解决方案:只有一个属性可以从'外部'设置ivar吗?

我已阅读:Must every ivar be a property?Property vs. ivar in times of ARC但无法得出最终结论。

回答

2

is the solution as simple as: only with a property can an ivar be set from 'outside'?

本质上说,是的。 Obj-C中的Ivars(默认情况下)是“protected”,这意味着编译器不允许你在对象自己的代码外部访问它们。例如,给定下面的类声明:

@interface Dunstable : NSObject 
{ 
    NSString * crunk; 
} 
@end 

你可能会觉得你能够创建对象后访问伊娃,而是试图将导致一个错误:

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已经过时了。)

可能使伊娃这是从对象外部访问:

@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文档包含一个“历史说明”这一点:

Note: Historically, the interface required declarations of a class’s instance variables, the data structures that are part of each instance of the class. These were declared in braces after the @interface declaration and before method declarations: [...] Instance variables represent an implementation detail, and should typically not be accessed outside of the class itself. Moreover, you can declare them in the implementation block or synthesize them using declared properties. Typically you should not, therefore, declare instance variables in the public interface and so you should omit the braces.

这是一个相当大的改变(实际上,我是惊讶,有没有在该文档中@interface宣布了高德给出的语法),但它绝对是更好的。你应该使用声明的属性;他们做正确的事情,并使您的代码更清洁和更安全。

+0

这真是一个很好的答案!现在我想知道ARC的到来,使用propet而不是必须保留和发布ivars,看看'[newCrunk retain]; [crunk release];'在你的示例中? – 2012-03-24 19:28:32

+0

是的,现在这种方法是完全不必要的(并且用ARC,非法写入)。这只是过去的一个例子。 – 2012-03-24 19:29:43

+0

allright =)我已经掌握了它,现在感谢你在这里的所有答案。感谢iulius提供最清晰,最完整的作品,并花时间去搜索历史笔记。其非常赞赏=) – 2012-03-24 19:38:15

2

当你写:创建

@synthesize name; 

伊娃的名字,并且它具有相同的名称属性。所以你可以通过或不通过自己访问它。

在现实中,如果你写

self.name = @"hello"; 

您正在访问的属性,如果你写

name = @"hello"; 

您正在访问的伊娃。大多数人(包括我)会建议你不要直接访问你的ivars,除非它真的是你想要的:例如,如果你正在为该属性创建一个自定义setter或getter。否则,总是自己访问该属性。

在我来说,我总是这样:

@synthesize name = _name; 

这种方法的优点是,当你忘了写自我,而不是访问伊娃,你会得到一个错误,告诉你,伊娃名称不存在。

+0

这是一个聪明的想法=)你明确定义头文件中的伊娃吗?或者你只是隐式让综合创建它? – 2012-03-24 19:25:29

+0

无需将它放在头文件中,合成将为您创建它! – Zalykr 2012-03-24 19:26:40

2

你不应该直接从课堂外访问ivars。这是属性的主要功能 - 定义访问方法供其他对象使用。但是,在同一个类中使用访问器也是一种很好的做法 - 这样可以确保发生任何适当的副作用(如果不使用ARC,内存管理就是一个明显的例子)。

所以,方法3通常是错误的。方法1大致相当于方法2 - 也就是幕后,运行时基本上是为您创建一个ivar。还要注意的是,你可以设置伊娃的名字,即使你没有明确定义它:

@interface{ 
//No ivar here! 
} 
@property (...) Type *name; 
@synthesize name = ivarName; 
+0

,我不知道。所以当我隐式给予ivar另一个名称而不是属性时,我将能够使用'self.name'和直接使用'ivarName'来访问它,并且从外部我将使用'setName'并使用属性。 – 2012-03-24 19:23:17

+1

是的,尽管'[self name]'和'self.name'通常是最好的选择,即使是在同一个班级中。 – andyvn22 2012-03-24 19:27:38

0

从您提供的第二个链接,Property vs. ivar in times of ARC,由丹尼斯·米哈伊洛夫在接受答案的评论是很能说明问题。他指出,与你的情况3,您可以通过访问伊娃:

classInstance->iVar = @"New value" 

但是,这被认为是不好的做法。所以我会重申你点为:

Only with a property should an ivar be set from 'outside'