2012-03-24 76 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'