2011-04-27 71 views
8

在閱讀初學者的iPhone開發者書籍並在線閱讀示例代碼之後,我注意到大多數Objective C程序員幾乎綜合了所有實例變量。一些變量可以方便地進行優化,但是在遵守面向對象的封裝原則時,大多數變量不應該這樣做。最差的是合成屬性標記爲私人。試圖使用別人代碼的C++程序員將讀取頭文件中的公共字段和方法。他們將跳過私有變量。此C++程序員不會知道您希望以某種有意義的方式使用私有屬性。是一個私人綜合財產矛盾嗎?

看看蘋果提供lazy table image loading此樣本模板:

@interface ParseOperation : NSOperation <NSXMLParserDelegate> 

{ 
@private 
    id <ParseOperationDelegate> delegate; 
    NSData   *dataToParse; 
    NSMutableArray *workingArray; 
    AppRecord  *workingEntry; 
    NSMutableString *workingPropertyString; 
    NSArray   *elementsToParse; 
    BOOL   storingCharacterData; 
} 

來源

@interface ParseOperation() 
@property (nonatomic, assign) id <ParseOperationDelegate> delegate; 
@property (nonatomic, retain) NSData *dataToParse; 
@property (nonatomic, retain) NSMutableArray *workingArray; 
@property (nonatomic, retain) AppRecord *workingEntry; 
@property (nonatomic, retain) NSMutableString *workingPropertyString; 
@property (nonatomic, retain) NSArray *elementsToParse; 
@property (nonatomic, assign) BOOL storingCharacterData; 
@end 

@implementation ParseOperation 
@synthesize delegate, dataToParse, workingArray, workingEntry, workingPropertyString, elementsToParse, storingCharacterData; 

現在我知道這是不是C++和我們不應該假定在Objective C中應該遵守所有的C++實踐目標C應該有充分的理由偏離一般編程實踐。

  1. 爲什麼全部是私人ivars合成?從整個項目來看,外部課程只使用NSMutableArray *workingArray。所以其他的ivars都不應該有setter和getters。
  2. 爲什麼非常敏感的ivars合成?首先,現在id delegate有一個setter,這個對象的用戶可以在XML解析中間切換委託,這是沒有意義的。另外,NSData *dataToParse是從網絡檢索到的原始XML數據。既然它有一個setter,這個對象的用戶可能會破壞數據。
  3. 什麼是標誌着包頭一切private的地步?因爲所有的ivars都被合成爲具有getter/setter,所以它們是公開的。你可以將它們設置爲任何你想要的,並且你可以隨時獲得它們的價值。
+0

昨天有一些類似的問題:[公開閱讀,私人保留](http://stackoverflow.com/questions/5788458/public-read-private-retain-property); [私人@property創建一個私人ivar?](http://stackoverflow.com/questions/5784875/does-a-private-property-create-an-private-instance-variable)我不確定是否他們雖然回答你的問題。 – 2011-04-27 17:10:21

+0

您可能也有興趣:[iOS:必須每個iVar真的是屬性?](http://stackoverflow.com/questions/5031230/ios-must-every-ivar-really-be-property) – 2011-04-27 19:56:38

回答

10

我遵循這個例子在我的很多課程中模仿的習慣用法,所以我可以試着解釋我自己爲這種做法的理由。

本示例中的屬性在.m文件的類擴展名中聲明。這使他們有效的私人。任何試圖從另一個類訪問這些屬性的嘗試都會在編譯時導致「Property not found」錯誤。

對於來自其他語言的開發人員,合成私有實例變量的getter和setter可能看起來很奇怪。事實上,我這樣做的唯一原因。一致使用時,綜合屬性可以簡化內存管理,並幫助避免可能導致錯誤的粗心錯誤。這裏有幾個例子:

考慮一下:

self.workingPropertyString = [NSMutableString string]; 

與此:

workingPropertyString = [[NSMutableString string] retain]; 

許多開發商會聲稱,這兩個任務相同的功能,但有一個重要的區別。如果workingPropertyString已經指向保留的對象,則第二個賦值會泄漏內存。要編寫代碼,功能上等同於合成的制定者,你必須做這樣的事情:

NSMutableString *newString = [NSMutableString string]; 
if (workingPropertyString != newString) { 
    [workingPropertyString release]; 
    workingPropertyString = [newString retain]; 
} 

此代碼避免泄露任何現有的對象實例變量可能指向,並安全地處理的可能性,您可能會將相同的對象重新分配給實例變量。合成的二傳手爲你做所有這些。

當然我們可以看到(workingPropertyString != newString)在這種情況下總是成立,所以我們可以簡化這個特定的任務。事實上,在大多數情況下,你可能會簡單地直接賦值給一個實例變量,但當然這是例外情況往往會造成最大的錯誤。我更喜歡玩它安全,並通過合成設置器來設置我所有的對象實例變量。我所有的實例對象分配是簡單的單行看起來像這樣:

self.foo = [Foo fooWithTitle:@"The Foo"]; 

或本:

self.foo = [[[Foo alloc] initWithTitle:@"The Foo"] autorelease]; 

這種簡單和一致給出了我的軟弱大腦少的東西要考慮。因此,我幾乎不會有與內存管理有關的錯誤。 (我知道autorelease這個習慣用法在理論上可以在嚴密的循環中消耗過多的內存,但在實踐中我還沒有遇到這個問題,如果我這樣做了,那麼優化就是一個簡單的例子。)

另一件事我喜歡這種做法,我的dealloc方法都這個樣子:

- (void)dealloc { 
    self.delegate = nil; 
    self.dataToParse = nil; 
    self.workingArray = nil; 
    self.workingEntry = nil; 
    self.workingPropertyString = nil; 
    self.elementsToParse = nil; 
    [super dealloc]; 
} 

編輯:丹尼爾Dickison指出了一些 風險使用的dealloc 存取,我還沒有考慮。請參閱 評論。

其中每個對象屬性簡單地設置爲零。這會同時釋放每個保留屬性,同時將其設置爲零以避免由於EXC_BAD_ACCESS導致的某些崩潰。

請注意,即使該屬性被聲明爲(nonatomic, assign),我也設置了self.delegate = nil;。這項任務不是絕對必要的。實際上,我可以完全不用我的(nonatomic, assign)對象的屬性,但是我又發現,在所有實例變量中一致地應用此慣用法使我的大腦更少思考,並進一步減少了創建錯誤通過一些粗心的錯誤。如果有必要,我可以簡單地將(nonatomic, assign)的屬性翻轉到(nonatomic, retain)而不必觸摸任何內存管理代碼。我喜歡。

也可以使用一致性作爲綜合私有標量變量屬性的參數,如您的示例在BOOL storingCharacterData;的情況下所做的那樣。這種做法可確保每個實例變量賦值看起來像self.foo = bar;。我通常不打算自己創建私有標量屬性,但我可以看到這種做法的一些理由。

+5

不錯的答案,但一個挑剔:設置屬性爲零,而不是直接在'dealloc'方法中調用'release'是不鼓勵的(有些蘋果文檔說,儘管我現在找不到它)。原因是setter可以發出KVO通知,導致觀察者嘗試訪問部分釋放的實例。 – 2011-04-27 19:54:55

+0

Ewww。好點子。我沒有用KVO做過多的工作,所以我沒有遇到過這種情況,但這很有道理。我將不得不尋找這些蘋果文檔,並可能重新考慮我如何編寫我的deallocs。感謝您讓我看到這種風險。 – cduhn 2011-04-27 20:06:42

+0

在這一點上,Apple的文檔似乎處於不一致的狀態。 Objective C Programming Language文檔說,如果你在現代運行時使用合成實例變量,「你必須調用訪問器方法」,因爲你不能直接訪問實例變量。然而,似乎在最新的工具中,你可以直接訪問伊娃,所以我想我在dealloc中使用的慣用法是'[伊娃爾釋放],伊娃=零;'。這裏有一個很好的討論:http://stackoverflow.com/questions/1283419/valid-use-of-accessors-in-init-and-dealloc-methods – cduhn 2011-04-27 20:54:07

0

爲什麼所有私人ivars 合成?當你看 項目作爲一個整體,只有 的NSMutableArray * workingArray由外類使用 。所以 其他ivars都不應該有setter和 獲得者。

沒有真正的需要;如果您要直接訪問所有ivars,則不需要@synthesize。

爲什麼非常敏感的ivars 合成?其一,現在ID 委託有一個二傳手的 用戶這個對象可以切換委託在 中間的XML解析,東西 是沒有意義的。另外,NSData * dataToParse是從網絡檢索的原始XML數據。現在它有一個 setter,這個對象的用戶可以用 破壞數據。

沒有公開聲明setter/getters。如果班上的一位客戶想要通過在中間切換委託來破壞事情,那麼他們必須打破封裝才能做到這一點。

因此,最終是一個非問題。

什麼意思在標題中標記一切 私人?由於所有ivars 都合成爲具有 獲取者/設置者,因此它們實際上是公開的 。你可以將它們設置爲你想要的任何東西 ,你可以隨時得到它們的值 。

請注意,在該示例中甚至不需要聲明ivars;編譯器會根據@property聲明自動合成它們。

傳統上,@私人保護免受有人直接從外部將伊娃從該類別的一個實例中剔除。

請注意,anInstance-> ivar或自體ivar幾乎從不使用(並且,在使用時,它幾乎總是出於錯誤原因)。有它的用途,但它很少見。

+0

似乎喜歡Objective C應該通過代碼佈局的方式*強制*封裝而不是*建議*封裝。在閱讀整個項目之後,我可以看到蘋果程序員*建議我不要篡改私有ivars,但我不應該那樣做。我應該可以只讀頭文件來理解如何使用這個類,並讓編譯器*強制封裝。 – JoJo 2011-04-27 18:10:12

+0

如果執行封裝,'anInstance-> publicIVar'和'self-> trulyPrivateIvar'有什麼問題? – JoJo 2011-04-27 18:13:47

+0

'anInstance-> publicIvar'不是使用的模式;它繞過了任何訪問方法,因此意味着子類無法覆蓋訪問或設置的行爲(這也意味着KVO將無法工作)。 「自我」是噪音;不必要。 – bbum 2011-04-27 19:27:19