2011-03-29 45 views
126

可能重複:
How does an underscore in front of a variable in a cocoa objective-c class work?爲什麼在iOS中使用前導下劃線重新命名合成的屬性?

當創建在Xcode 4一個新的項目,樣板代碼添加下劃線時,它綜合了在實現文件中實例變量爲:

@synthesize window = _window; 

或者:

@synthesize managedObjectContext = __managedObjectContext; 

有人能告訴我這裏有什麼成就嗎?我不是一個完整的nube,但這是我不明白的Objective-C的一個方面。

另一個混亂點;在應用程序委託實現中,合成窗口的Ivar如上,在應用didFinishLaunchingWithOptions後:方法窗口和ViewController實例變量是指使用自:

self.window.rootViewController = self.viewController 
[self.window makeKeyAndVisible]; 

但在dealloc方法是_window,或_viewController

由於

回答

223

這是以前版本的Objective-C運行時的工件。

本來@synthesize用於創建存取方法,但運行時仍要求實例變量必須顯式實例:

@interface Foo : Bar { 
    Baz *_qux; 
} 

@property (retain) Baz *qux; 
@end 

@implementation Foo 
@synthesize qux = _qux; 

- (void)dealloc { 
    [_qux release]; 
    [super dealloc]; 
} 

@end 

人們會前綴及其實例變量從他們的性質區分開來(即使Apple不希望你使用下劃線,但這是另一回事)。您將該屬性綜合爲指向實例變量。但重點是,_qux是一個實例變量,並且self.qux(或[self qux])是發送到對象self的消息qux

我們直接使用實例變量-dealloc;使用訪問方法,而不是看起來像這樣(雖然我不建議這樣做,原因稍後我會介紹):

- (void)dealloc { 
    self.qux = nil; // [self setQux:nil]; 
    [super dealloc]; 
} 

這有釋放qux,以及清零參考的作用。但是這可能會帶來不幸的副作用:

  • 您可能最終會觸發一些意外通知。其他對象可能會觀察對qux的更改,當使用訪問器方法更改該對象時會記錄這些更改。
  • (並非所有人都同意這一點:)清零指針作爲訪問者可能會隱藏您的程序中的邏輯錯誤。如果你曾經訪問過一個對象的實例變量該對象已被釋放,你正在做一件嚴重錯誤的事情。但是,由於Objective-C的nil -messaging語義,您永遠不會知道,使用訪問器設置爲nil。如果你直接發佈了實例變量而沒有清零引用,那麼訪問釋放對象會導致響亮的響應EXC_BAD_ACCESS

更新版本的運行時添加了除訪問方法外還綜合實例變量的功能。與這些版本的運行時,上述代碼可以被寫入省略實例變量:

@interface Foo : Bar 
@property (retain) Baz *qux; 
@end 

@implementation Foo 
@synthesize qux = _qux; 

- (void)dealloc { 
    [_qux release]; 
    [super dealloc]; 
} 

@end 

這實際上合成上Foo一個實例變量稱爲_qux,其通過獲取和設置消息-qux-setQux:訪問。

我推薦這樣:它有點混亂,但有一個很好的理由使用下劃線;即防止意外直接接觸伊娃。如果你認爲你可以信任自己記住無論你是使用原始實例變量或存取方法,只是不喜歡這樣,而不是:

@interface Foo : Bar 
@property (retain) Baz *qux; 
@end 

@implementation Foo 
@synthesize qux; 

- (void)dealloc { 
    [qux release]; 
    [super dealloc]; 
} 

@end 

然後,當你想直接訪問實例變量,只是例如qux(其在C語法中轉換爲self->qux以從指針訪問成員)。當你想使用訪問器方法(它會通知觀察者,並做其他有趣的事情,並使內存管理更安全和容易),請使用self.qux[self qux])和self.qux = blah;[self setQux:blah])。

這裏最令人難過的是蘋果的示例代碼和模板代碼很糟糕。切勿將其用作正確的Objective-C風格的指南,並且絕對不要將其用作適當軟件體系結構的指南。 :)

+59

沒有爲'@合成QUZ = _quz一個很好的理由; ';它意味着'self.quz'意思是消除了'quz'的意外寫入,反之亦然。編譯器問題相對較短,但真實。如果您發現有borked的示例,請提交bug。 – bbum 2011-03-29 01:11:41

+1

@bbum好點再下劃線命名。我通常相信自己要打出正確的東西(或者如果我搞砸了,至少可以解決它),但是在制定編碼風格時,這肯定是需要思考的問題(我傾向於在美學方面犯錯誤,但它完全有效傾向於防止事故)。 – 2011-03-29 05:58:03

+0

如果你不想手動編寫所有的樣板文件,你可能需要使用https://github.com/holtwick/xobjc(無恥的自我宣傳的開源項目;))它支持前導和尾部下劃線。 Google Styleguide爲ObjC推薦了後面的內容。這也很有用,因爲Apple在自己的代碼中也使用了前導下劃線,所以在極少數情況下您可能會遇到私有API的麻煩。 – Holtwick 2011-03-29 08:21:30

6

在應用didFinishLaunchingWithOptions:方法中的窗口和ViewController的ivars被稱爲使用自

不,他們不是。這些是對屬性windowviewController的引用。這是下劃線的要點,使用該屬性時(無下劃線)以及當直接訪問ivar(帶下劃線)時更加清晰。

2

是的,它只是爲了區分對象的參考。也就是說,如果直接引用該對象,則使用下劃線,否則使用self來引用該對象。

13

這是另一個原因。如果不強調實例變量您經常獲得與參數self.title = titleself.rating = rating警告:

@implementation ScaryBugData 
@synthesize title; 
@synthesize rating; 
- (id)initWithTitle:(NSString *)title rating:(float)rating { 
    if (self = [super init]) { 
     self.title = title; // Warning. Local declaration hides instance variable 
     self.rating = rating; // Warning. Local declaration hides instance variable 
    } 
    return self; 
} 
@end 

您可以通過強調實例變量避免警告:

@implementation ScaryBugData 
    @synthesize title = _title; 
    @synthesize rating = _rating; 
    - (id)initWithTitle:(NSString *)title rating:(float)rating { 
     if (self = [super init]) { 
      self.title = title; // No warning 
      self.rating = rating; // No warning 
     } 
     return self; 
    } 
    @end 
+0

必須愛從[雷文德里希偉大的可怕的錯誤的應用程序教程]示例代碼(http://www.raywenderlich.com/1797/ios-tutorial-how-to-create-a-simple-iphone-app-part-1 ):) – nicksuch 2014-09-09 23:11:52

相關問題