標籤:
執行個體變數:
屬性其實說直白點就是 ivar + setter + getter(執行個體變數+存取方法),不過在OC中屬性多了字面量這一系列特殊關鍵字使得OC屬性有些不同。
成員屬性我們應該都使用過,比如現在定義一個Car類有name和speed成員變數:
#import <Foundation/Foundation.h>@interface Car : NSObject{ @public NSString *name; NSInteger speed;}@end
在OC類的內部有一個位移量,專門標記成員變數在記憶體中的所在位置。如果現在在添加一個新的成員變數在name的前面,那麼就會出現位移量整體便宜的問題,現在添加一個price執行個體:
#import <Foundation/Foundation.h>@interface Car : NSObject{ @public NSInteger price; NSString *name; NSInteger speed;}@end
此時位移量在記憶體中顯示如下:
Car |
Car |
name |
price |
speed |
name |
|
speed |
可以看到執行個體位移量發生了改變,但是OC將執行個體變數作為一種儲存位移量所用的“特殊變數”,交個類對象(class object)保管,位移量會在運行時尋找,所以總能正確的找到位移量。
@Property
使用屬性相比成員變數更加抽象,能夠使用setter和getter對變數做更多的處理。
說一下屬性的特性
@synthesize關鍵字
該關鍵字指定了屬性的執行個體變數名稱,並且根據儲存語義(readwrite、readonly)系統自動合成setter和getter方法,當然也可以手寫來覆蓋系統提供的。
@dynamic
該關鍵字告訴編譯器不要為我合成setter和getter方法,這些方法將由我自己實現。當然我們可以不實現這在編譯階段不會出現問題,直到運行時才會檢查是否實現了setter和getter,如果沒有實現就會拋出異常。
例如在CoreData中NSManagedObject子類的所有屬性全部都是dynamic標記的,這是因為子類的某些屬性不是
屬性特性(語義)
屬性的特質分為四類:
1.原子性:
原子性就是指該屬性是否為同步的,OC中大部分屬性都是nonatomic(非原子性)的,如果不寫nonatomic那麼就會是原子性的。理論上來說原子性屬性的讀寫都將會是同步的,但是OC中atomic並不能一定確定屬性為同步的,如果真要進行同步操作,還要用更加深層次的同步鎖API。而且atomic會很影響效率,所以一般都會寫nonatomic。
2.讀/寫入權限:
讀寫為readonly和readwrite兩種,前一種在系統只會合成getter方法,而後一種則會同時產生setter和getter。如果屬性設定為了readonly屬性,那麼該屬性是不可以修改的。
3.記憶體管理語義:
assign:該方法只會針對“純量類型”(CGFloat或NSInteger等)的簡單賦值操作,id類型也要用assign,所以一般iOS中的代理delegate屬性都會用assign來標示,如:
@property (nonatomic, assign) id <UITableViewDataSource> dataSource;@property (nonatomic, assign) id <UITableViewDelegate> delegate;
strong: 使用該特性執行個體變數在賦值時,會釋放舊值同時設定新值,對對象產生一個強引用,用MRC來說就是引用計數+1。
weak: 屬性工作表明了一種”非擁有關係“,既不釋放舊值,也不保留新值。用MRC就是引用計數不變,當指向的對象被釋放時,該屬性自動被設定為nil。這裡多說一點,weak的runtime實現是通過hash表完成的,用變數名做鍵,一旦發現屬性所指的對象被釋放了,立刻設定為nil。
unsafe_unretained:和weak一樣,唯一的區別就是當對象被釋放後,該屬性不會被設定為nil。所以是unsafe的。
copy:和strong類似,不過該屬性會被複製一個新的副本。很多時使用copy是為了方式Mutable(可變類型)在我們不知道的情況下修改了屬性值,而用copy可以產生一個不可變的副本防止被修改。如果我們自己實現setter方法的話,需要手動copy。
4.方法名:
getter = <name>
setter = <name>
方法名可以修改為我們合成的方法名,可以使存取方法語義更加符合應用情境。
如果要在其它屬性裡面設定屬性的話,還是要符合屬性特性,比如copy的話我們還是要手動copy一下屬性。這裡說一下構造方法裡需要直接操作執行個體變數,而不應該調用setter和getter。
對象內部盡量直接存取執行個體變數
首先說一下構造方法和析構方法中為什麼不能使用setter和getter,因為setter和getter是經過我們封裝過的方法,有可能增加一些判斷,而如果子類調用父類的構造方法同時實現了自己的setter和getter,那麼很可能就會出現問題。
通過屬性訪問執行個體變數會使用屬性的字面語義,會使用KVO所以在執行效率上肯定比直接調用執行個體變數慢,但是通過屬性訪問可以截獲屬性的擷取和設定更加方便調試和控制。
一般在類內部推薦設定用setter 擷取直接用執行個體變數。
這裡再說一下惰性載入,所謂惰性載入就是指,屬性會在第一次調用getter的時候初始化,如下:
-(NSString *)name{ if (!_name){ _name = [[NSString alloc] init]; } return _name;}
那麼此時就只能夠通過getter來調用執行個體變數了。
Objective-C—— @Property詳解