在對象內(nèi)部盡量直接訪問實例變量

在對象之外訪問實例變量時,總是應(yīng)該通過屬性來做.然而在對象內(nèi)部訪問實例變量時,又該如何呢?這個問題在OC的開發(fā)中一直存在爭論,筆者認為:

除了幾種特殊情況之外,強烈建議大家在讀取實例變量時采用直接訪問的形式,而在設(shè)置實例變量的時候通過屬性來做.

請看線面這個類:

@interface CWGPerson : NSObject
@property (nonatomic, copy) NSString *firstName;
@property (nonatomic, copy) NSString *lastName;

- (NSString *)fullName;
- (void)setFullName:(NSString *)fullName;
@end

fullName與setFullName這兩個"便捷方法"可以這樣來實現(xiàn):

- (NSString *)fullName {
  return [NSString stringWithFormat:@"%@, %@", self.firstName, self.lastName];
}

- (void)setFullName:(NSString *)fullName {
  NSArray *components = [fullName componentsSeparateByString:@" "];
  self.firstName =  components[0];
  self.lastName = components[1];
}

在fullName的獲取方法中設(shè)置方法中, 我們使用"點語法", 通過存取方法來訪問相關(guān)實例變量.現(xiàn)在假設(shè)重寫這兩個方法, 不經(jīng)過存取方法,而是直接訪問實例變量:

- (NSString *)fullName {
  return [NSString stringWithFormat:@"%@, %@", _firstName, _lastName];
}

- (void)setFullName:(NSString *)fullName {
  NSArray *components = [fullName componentsSeparateByString:@" "];
  _firstName =  components[0];
  _lastName = components[1];
}

這兩種寫法有幾個區(qū)別:

  • 由于不經(jīng)過OC的"方法派發(fā)"步驟,所以直接訪問實例變量的速度當然快,在這樣情況下,編譯器所生成的代碼會直接訪問班車對象實例變量的那塊內(nèi)存.
  • 直接訪問實例變量時,不會調(diào)用"設(shè)置方法",這就繞過了為相關(guān)屬相所定義的"內(nèi)存管理語義".比如說:如果在ARC下直接訪問一個聲明為copy的屬性,那么并不會拷貝該屬性,只會保留新值并釋放舊值.
  • 如果直接訪問實例變量,那么不會觸發(fā)"鍵值觀察者(KVO)通知",這樣做是否會產(chǎn)生問題,還取決于具體對象行為.
  • 通過屬性來訪問有助于排查與之相關(guān)的錯誤,因為可以在"獲取方法"和"設(shè)置方法"中加斷點,跟蹤該屬性調(diào)用者及其訪問時機.

說了這么多,其實有一種合理的折中方法:在寫入實例變量時,通過其"設(shè)置方法"來做,而在讀取實例變量時,則直接訪問它.此辦法既能提高讀取操作速度,又能控制對屬性的寫入操作.之所以要通過"設(shè)置方法"來寫入實例變量,其首要原因在于:這樣能確保相關(guān)屬性的"內(nèi)存管理語義"得以貫徹.但是,選用這樣的做法,還是要注意幾個問題:

  • 在初始化方法中該如何設(shè)置屬性值.這種情況綜述應(yīng)該直接訪問實例變量,因為子類可能會"覆寫"設(shè)置方法,假設(shè)CWGPerson有一個子類叫做CWGSmithPerson,這個子類專門表示那些姓"Smith"的人,該子類可能會覆蓋lastName屬性所對應(yīng)的設(shè)置方法:
-(void)setLastName:(NSString *)lastName {
  [super setLastName:lastName];
  if(![lastName isEqualToString:@"Smith"]) {
   [NSException raise:NSInvalidArgumentException format:@"Last name must be Smith"];
}

在基類CWGPerson的默認初始化方法中,可能會將姓氏設(shè)為空字符串,此時若是通過"設(shè)置方法"來做,那么調(diào)用的將會是子類的設(shè)置方法,從而拋出異常,但是密歇情況下卻必需在初始化方法中調(diào)用設(shè)置方法:如果待初始化的實例變量聲明在超類中,而我們又無法再子類中直接訪問此實例變量的話,那么就要調(diào)用"設(shè)置方法"了.

  • 另外一種就是"惰性初始化".在這樣的情況下必需通過"獲取方法"來訪問屬性.否則實例變量就永遠無法初始化.

總結(jié):

  • 在對象內(nèi)讀取數(shù)據(jù)時, 應(yīng)該直接通過實例變量來讀,而寫入數(shù)據(jù)時,則應(yīng)該通過屬性來寫.
  • 在初始化方法及dealloc方法中,總是應(yīng)該直接通過實例變量來讀寫數(shù)據(jù)(除一些特殊子類中).
  • 有時會使用惰性初始化技術(shù)配置數(shù)據(jù),這樣的情況下,需要通過屬性來讀取數(shù)據(jù).
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容