理解屬性property

可以用@property語法來定義對象中所封裝的數據
通過"特質(arrribute)"來指定存儲數據所需的正確語義
在設置屬性所對應的實例變量時,一定要遵從該屬性所聲明的語義
在開發(fā)iOS程序時應該使用nonatomic屬性,因為atomic會嚴重影響性能
在對象內部盡量直接訪問實例變量

"屬性(property)"是Objective-C的一項特性,用于封裝對象中的數據。Objective-C對象通常會把其所需要的重要數據保存為各種實例變量。實例變量一般通過存取(getter和setter)方法來訪問

在描述個人信息時,通過存放人名,生日,地址等內容,可以在類接口的public區(qū)段聲明一些實例變量

@interface:NSObject{
@public
    NSString *_firstName;
    NSString *_lastName;
@private
    NSString *_someIntenalData;
}
@end

這種編寫風格類似于C++和Java。但是Objective-C幾乎不會這樣做,因為這樣的寫法,對象布局在編譯器就已經確定,當要訪問一個屬性的時候,使用的是硬編碼之后的偏移量(offset),表示該變量存放在對象起始地址有多遠。如果要在_fisrtName前面再加上一個變量,那么之前所指的偏移量都指向了錯誤的地址,修改之后必須重新編譯。

例如,某個代碼庫中的代碼使用了一份舊的類定義。如果與之相連接的代碼使用了新的定義,那么運行的時候就會出現不兼容的現象。各種語言均有相應的解決方法.Objective-C的做法是把存儲偏移量所用的"特殊變量"交給類對象來保存。偏移量會在運行時查找,如果類定義變了,那么存儲的偏移量也就變了,這樣可以保證何時訪問實例變量,總能找到正確的偏移量。甚至可以在運行期向類中新增實例變量,這就是穩(wěn)固的"應用程序二進制接口(Application Binary Interface,ABI)"
正是因為這些特性,使得我們可以在"類擴展(class-continuation)"或實現文件中定義實例變量。所以說,不一定要在接口中把全部實例變量都聲明好,可以將某些變量從接口的public區(qū)段移走,以便保護與類實現有關的內部信息。
盡量不要對象外部直接訪問實例變量,而應該通過存取方法來做(因為運行時機制),可以使用@property語法來編寫實例變量,@property會自動生成對應的存取方法。
例如:

@interface Person:NSObject
@property  NSString *firstName;
@end

對于類的使用者來說,相當于

//Person.h
@interface Person:NSObject
- (NSString *)firstName;
- (void)setFirstName:(NSString*)firstName;
@end
//Person.m
@implement Person
@synthesize _firstName;
@end

如果讀寫用@property定義的屬性,可以使用點語法,編譯器會自動將點語法轉換為對應的存取方法;

Person *person = [[Person alloc]init];
NSString * temp = person.firstName;
person.firstName = @"Test";

編譯后會變成

Person *person = [[Person alloc]init];
NSString * temp = [person firstName];
[person setFirstName:@"Test"];

屬性特質

使用屬性時還有一個問題需要注意,就是其各種特質(arrribute)設定也會影響編譯器所產生的存取方式,屬性可以擁有的特質分為以下四類

原子性

在默認情況下,由編譯產生的合成方式會通過鎖定機制來確保其原子性(atomic),如果屬性具備nonatomic特質,則不使用同步鎖。
如果開發(fā)iOS應用,你會發(fā)現,其中所有的屬性都聲明為nonatomic。這樣做是因為在開發(fā)iOS中使用同步鎖的開銷較大,這會帶來性能問題,一般情況下并不要求屬性必須是"原子的",因為這并不能保證線程安全,如果要實現線程安全的操作,必須使用更深層次的鎖定機制才可以。也就是說,一個線程在連續(xù)多次讀取某屬性的過程中,有別的線程在同時改寫該值,那么即使將屬性聲明為atomic,也還是會讀到不同的屬性值。因此在開發(fā)iOS程序時,一般都會使用nonatomic屬性。但是在開發(fā)macOS程序的時候,使用atomic一般不會有性能瓶頸。

讀/寫權限

  • readwrite(讀寫)特質的屬性擁有獲取(getter)方法和設置(setter)方法,這是默認的讀寫屬性,如果不指明,默認就是readwrite
  • readonly只讀,只有讀取方法,沒有對外的設置方法

內存管理語義

屬性用于封裝數據,而數據則要有"具體的所有權語義"

  • assign 設置方法只會針對"純量類型"(scalar type,例如CGFloat或者NSInteger等)進行簡單的賦值操作,Objective-C的基本類型和所有在棧上存儲的變量使用這個特質
  • strong 表示擁有關系,強引用等同于retain
  • weak 表示弱引用,對象不擁有該屬性,對象遭到摧毀的時候,屬性值也會設為nil
  • unsafe_unretained 該特質語義和assign類似,但是它適用于"對象類型",該特質表示一種非擁有關系,與weak的區(qū)別在于,對象摧毀的時候,屬性值不會被設置為nil(unsafe)
  • copy 此特質表達所屬關系和strong類似,但是是保留一份拷貝的值,NSString一般要使用該特質

方法名

@property默認為屬性生成getter和setter方法名,如果需要自行制定getter和setter方法的方法名,可以使用該特質

  • getter = < name >:指定"獲取方法"的方法名,一般屬性是Boolean型,而你想為獲取方法加上"is"前綴,那么可以使用該特質
@property (nonatomic,getter = isOn) BOOL on;
  • **setter = < name > **:指定設置方法使用的方法名,不常用

屬性在對象內部的訪問

使用@property生成的變量,在實現文件中會自動生成一個以"_"開頭的同名變量(見上述)。那么,在對象內部是直接訪問還是使用屬性訪問好呢。
例如

@interface Person:NSObject
@property (nonatomic,copy) NSString *firstName;
@property (nonatomic,copy) NSString *lastName;
- (NSString *) fullName;
@end

fullName方法在內部有兩種實現方式

//方法一:使用屬性訪問
- (NSString *)fullName
{
    return [NSString stringWithFormat:@"%@ %@",self.firstName,self.lastName];  
}

//方法二:使用直接訪問
- (NSString *)fullName
{
    return [NSString stringWithFormat:@"%@ %@",_firstName,_lastName];  
}

這兩個寫法的區(qū)別在于

  • 直接訪問不經過Objective-C的"方法派發(fā)",所以速度更快一些
  • 直接訪問實例變量,不會調用其"設置方法",這就繞過了相關屬性所定義的"內存管理語義",比方說,如果在ARC下直接訪問一個聲明為copy的屬性,那么并不會拷貝該屬性,只會保留新值釋放舊值。
  • 如果直接訪問實例變量,不會觸發(fā)KVO
  • 通過屬性來訪問有助有排查與之相關的錯誤,因為可以給getter和setter設置斷點

可以看出,屬性訪問和直接訪問各有優(yōu)勢,一種折中的方式是:

  • 寫入實例變量的時候,使用屬性訪問
  • 讀取實例變量的時候,直接訪問。

這樣既能提高讀取操作的速度,又能控制對屬性的寫入操作。

需要注意的是,

  • 在初始化中應該如何設置屬性值,這種情況下,一般使用直接訪問實例變量來讀寫變量,因為子類可能會overwrite屬性的設置方法。
  • 懶加載(lazy initialzation), 在懶加載的情況下,應該使用"獲取方法"來訪問屬性。否則,實例變量就永遠不會被初始化。

懶加載:對于有些變量,如果使用頻率較低,而且創(chuàng)建該變量的成本較高,那么應該使用懶加載,在內部其他地方使用懶加載的變量的時候,不能使用直接訪問的方式來訪問,而是要通過屬性(獲取方法getter)來訪問(注意,只有getter內部可以直接訪問變量),例如,如果Person對象有個brain屬性,懶加載的getter可以這樣寫

- (Brain *)brain
{
    if(!_brain){
        _brain = [Brain new];
    }   
    return _brain;
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容