爭論:在對象之外訪問實(shí)例變量時,總是應(yīng)該通過屬性來做.然而在對象內(nèi)部訪問實(shí)例變量時,又該如何呢?這個問題在OC的開發(fā)中一直存在爭論,筆者認(rèn)為:
除了幾種特殊情況之外,強(qiáng)烈建議大家在讀取實(shí)例變量時采用直接訪問的形式,而在設(shè)置實(shí)例變量的時候通過屬性來做.
<h5>方法探討:</h5>
請看下面這個類:
<pre><code>@interface CWGPerson : NSObject</code>
<code>@property (nonatomic, copy) NSString *firstName;</code>
<code>@property (nonatomic, copy) NSString *lastName;</code>
<code>- (NSString *)fullName;</code>
<code>- (void)setFullName:(NSString *)fullName;</code>
<code>@end</code></pre>
- 通過存取方法來實(shí)現(xiàn)fullName的setter和getter方法:
<pre><code>- (NSString *)fullName {</code>
<code> return [NSString stringWithFormat:@"%@, %@", self.firstName, self.lastName];
</code>
<code>}</code>
<code>- (void)setFullName:(NSString *)fullName {</code>
<code> NSArray *components = [fullName componentsSeparateByString:@" "];</code>
<code> self.firstName = components[0];</code>
<code> self.lastName = components[1];</code>
<code>}</code></pre>
- 直接訪問實(shí)例變量實(shí)現(xiàn)fullName的setter和getter方法:
<pre><code> - (NSString *)fullName {
</code>
<code> return [NSString stringWithFormat:@"%@, %@", _firstName, _lastName];</code>
<code>}</code>
<code>- (void)setFullName:(NSString *)fullName {</code>
<code> NSArray *components = [fullName componentsSeparateByString:@" "];</code>
<code> _firstName = components[0];</code>
<code> _lastName = components[1];</code>
<code>}</code></pre>
這兩種實(shí)現(xiàn)區(qū)別:
- 直接訪問實(shí)例變量速度快:因?yàn)椴唤?jīng)過OC的"方法派發(fā)"步驟,編譯器所生成的代碼會直接訪問保存對象實(shí)例變量的那塊內(nèi)存.
- 直接訪問實(shí)例變量繞過了屬性“內(nèi)存管理語義”:直接訪問屬性不會調(diào)用setter方法,這就繞過了為相關(guān)屬相所定義的"內(nèi)存管理語義".比如說:如果在ARC下直接訪問一個聲明為copy的屬性,那么并不會拷貝該屬性,只會保留新值并釋放舊值.
- 直接訪問實(shí)例變量不會觸發(fā)"鍵值觀察者(KVO)通知":這樣做是否會產(chǎn)生問題,還取決于具體對象行為.
- 通過屬性來訪問有助于排查與之相關(guān)的錯誤:因?yàn)榭梢栽?獲取方法"和"設(shè)置方法"中加斷點(diǎn),跟蹤該屬性調(diào)用者及其訪問時機(jī).
<h5>合理方法:</h5>
- 說了這么多,其實(shí)有一種合理的折中方法:
在寫入實(shí)例變量時,通過其"設(shè)置方法"來做,而在讀取實(shí)例變量時,則直接訪問它.
- 優(yōu)勢:
此辦法既能提高讀取操作速度,又能控制對屬性的寫入操作.
<h5>注意問題:</h5>
之所以要通過"設(shè)置方法"來寫入實(shí)例變量,其首要原因在于:這樣能確保相關(guān)屬性的"內(nèi)存管理語義"得以貫徹.但是,選用這樣的做法,還是要注意幾個問題:
- 在初始化方法init和dealloc方法中應(yīng)該直接通過實(shí)例變量來訪問.
<pre><code>// init</code>
<code>-(id)init {</code>
<code> self = [super init];</code>
<code> if (self) {</code>
<code> _count = [[NSNumber alloc] initWithInteger:0];</code>
<code> }</code>
<code> return self;</code>
<code>}</code>
<code> dealloc</code>
<code>- (void)dealloc {</code>
<code> [_count release];</code>
<code> [super dealloc];</code>
<code>}</code></pre>
在 init 和 dealloc 中,對象的存在與否還不確定,所以給對象發(fā)消息可能不會成功牵现。
- 另外一種就是"惰性初始化".在這樣的情況下必需通過"獲取方法"來訪問屬性.否則實(shí)例變量就永遠(yuǎn)無法初始化.
<pre><code>-(NSArray *)array{</code>
<code> if (!_array) {</code>
<code> _array = @[@"aa",@"bb"];</code>
<code> }</code>
<code> return _array;</code>
<code>}</code></pre>
<h5>總結(jié):</h5>
- 在對象內(nèi)讀數(shù)據(jù), 應(yīng)該直接通過實(shí)例變量來讀,而寫數(shù)據(jù)時,則應(yīng)該通過屬性來寫.
- 在初始化方法及dealloc方法中,總是應(yīng)該直接通過實(shí)例變量來讀寫數(shù)據(jù)(除一些特殊子類中).
- 有時會使用惰性初始化技術(shù)配置數(shù)據(jù),這樣的情況下,需要通過屬性來讀取數(shù)據(jù).