Apple官方文檔 這個標題翻譯成中文就是 :面試
屬性 = 實例變量 + 存取方法
複製代碼
當咱們在一個類中定義屬性(name)時,系統會自動爲咱們自動生成一個帶下劃線的屬性 (_name) 並添加這個屬性的存取方法 (setter、getter) 。這個過程叫作 autosynthesis (自動合成),在代碼的編譯期執行。安全
這就引入了咱們接下來要說的 @sythesize 。 但咱們仍是先說說@property的關鍵字:bash
原子性 在操做系統的學習中咱們瞭解到,一個操做具備原子性代表這個操做不可分割,即最基本的操做。這關乎到線程是否安全的問題:多個線程可能同時訪問一個屬性。若是一個屬性不具有原子性,就不會使用同步鎖。在iOS開發時,咱們極大多數狀況都用nonatomic非原子性,而在OS X開發時,會使用到atomic原子性。這樣作的緣由是在iOS中使用同步鎖,性能開銷較大,在極少數須要加鎖保護線程安全的時候,咱們還須要更底層的鎖機制。app
可讀、可寫權限 這個特質有兩個選項,readwrite和readonly,分別對應讀寫權限和只讀權限。只讀權限編譯器並不會合成「setter」。默認狀況,該特質爲readwrite。post
setter方法中賦值的方式,決定了內存管理中引用計數的變化。性能
這個特質有很是多的選項,咱們依次說明。學習
assign:只對簡單數據類型進行賦值操做。簡單數據類型包括NSInteger、CGFloat等。 weak:不對該屬性具有擁有關係。此時的setter方法並不會改變對象的引用計數,若是該對象的擁有者全都release了,那麼該對象會完全釋放,該weak指針會被置爲空nil。ui
strong:對該屬性具有擁有關係。此時的setter方法會retain新的對象,release舊的對象。若是舊的對象以前引用計數爲1,那麼在此次從新賦值時就會被完全釋放。atom
copy:在setter中,會把該對象複製一份並賦給該屬性。咱們說複製有兩種,『淺拷貝』和『深拷貝』,『淺拷貝』是指對指針的複製,對象自己的內存區域沒有變化,新指針指向的仍是原有內存區域;『深拷貝』是指對內存區域存儲的數據進行的複製,指針會指向新的內存區域。特別注意的是,若是是對可變對象的使用copy特質不會改變引用計數,爲深拷貝;若是是不可變對象使用copy特質,就是淺拷貝,引用計數++,此時和strong徹底相同。一般狀況下,咱們對可變對象使用copy特質。當類型爲NSString時,咱們也應該對其加以copy特質,由於setter中的新值多是一個NSMutableString。spa
unsafe_unretained:不對該屬性具有擁有關係,但和weak的區別在於指針所指向的對象被完全釋放時,該屬性的指針不會被置爲空nil。
retain:這是之前在MRC內存管理方式下的屬性,在setter方法中對新值retain。ARC中已經棄用! 關於可變對象要使用copy特質:爲了保證封裝性,setter方法是屬性的設值方法,但可變對象可能會在不通過setter方法的狀況下,改變自身的值,這樣破壞了原有的封裝性。因此要對可變對象作copy操做,深拷貝出一份不可變的對象。
在之前 @property 要和 @synthesize 搭配使用,iOS 6 以後編譯器引入了property autosynthesis,即屬性自動合成。編譯器會自動給 @property 添加 @synthesize :
@synthesize propertyName = _propertyName;
複製代碼
這行代碼會在編譯期間創造一個帶下劃線的實例變量名,同時使用這個屬性生成getter 和 setter 方法。因此如今,咱們的代碼中已經不多看到 @synthesize 了。 若是你不喜歡這個帶下劃線的名字,你也能夠本身來指定喜歡的名字:
@synthesize propertyName = propertyName;
複製代碼
但帶下劃線的命名方式已經成爲開發者們約定俗成的規範,若是沒有特殊要求,仍是不要使用@synthesize ,而是直接聲明屬性@property。
當有自定義的 setter 或 getter 方法時 ,會屏蔽自動生成改方法。
setter 或 getter 是不能同時重寫的,不然編譯器不會自動生成該屬性。
KVO全稱是Key-Value Observing,是Objective-C語言中的一種核心機制,咱們通常翻譯爲鍵值觀察。至於KVO的實現方式,相信不少人也知道,就是繼承該類並重寫setter方法,當調用setter方法的時候,通知監聽器回調。前文說到了,@property能夠自動合成setter,因此這裏正是它和KVO之間千絲萬縷的關係。
咱們訪問變量有幾種方式,遵循Objective-C調用方法的方式訪問[self variableName]是一種,self.variableName語法糖是一種,直接訪問實例變量_variableName也是一種,這三種方式有什麼區別呢?
方法調用[self propertyName]
複製代碼
如前文所講,這種方式正是使用了getter方法,經過getter返回值的來訪問到實例變量的值,遵循良好的封裝性。
點語法糖self.propertyName
複製代碼
正如它的名字同樣,這種方式其實只是一種語法糖,其背後根本上仍是調用getter方法。
直接訪問實例變量_variableName不通過取設方法,直接訪問實例變量所在內存位置。優勢在於不通過方法調用,速度更快。
一樣看一下設值的方式呢,也有以下三種:方法調用
[self setPropertyName]
複製代碼
使用setter來設值。 點語法糖
self.propertyName = @"Name"
複製代碼
語法糖,背後一樣是setter方法。 直接訪問實例變量
_propertyName = @"Name"
複製代碼
不通過設值方法,將新值賦給實例變量所在內存區域。優勢仍是不通過方法調用,速度更快。然而問題也很明顯,用這種方式改變值KVO失效,跳過了setter中內存管理的特質。好比你給某個字符串賦了mutable可變字符串,若該屬性內存管理特質爲copy,此時經過setter設值會深拷貝一份不可變字符串,但直接訪問該實例變量不經過setter會致使沒有拷貝出一份不可變字符串。
對於這樣的問題,咱們大概能夠總結出最佳使用姿式:
在須要設值的地方,咱們經過setter設值方法設值;當須要取值的時候,咱們採用直接訪問實例變量的方式。
另外要注意,初始化方法中,沒有特殊須要,應該儘可能直接訪問實例變量。
告訴編譯器不須要自動合成屬性的 setter 和 getter 方法,而由開發之本身動態綁定。 須要注意的是,編譯器不會自動生成 屬性 因此還須要手動定義屬性。
更詳盡的資料這裏有