@property @synthesize @dynamic

@property = ivar + setter + getter

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操做,深拷貝出一份不可變的對象。

@synthesize 屬性自動合成

在之前 @property 要和 @synthesize 搭配使用,iOS 6 以後編譯器引入了property autosynthesis,即屬性自動合成。編譯器會自動給 @property 添加 @synthesize :

@synthesize propertyName = _propertyName;
複製代碼

這行代碼會在編譯期間創造一個帶下劃線的實例變量名,同時使用這個屬性生成getter 和 setter 方法。因此如今,咱們的代碼中已經不多看到 @synthesize 了。 若是你不喜歡這個帶下劃線的名字,你也能夠本身來指定喜歡的名字:

@synthesize propertyName = propertyName;
複製代碼

但帶下劃線的命名方式已經成爲開發者們約定俗成的規範,若是沒有特殊要求,仍是不要使用@synthesize ,而是直接聲明屬性@property。

當有自定義的 setter 或 getter 方法時 ,會屏蔽自動生成改方法。

注意

setter 或 getter 是不能同時重寫的,不然編譯器不會自動生成該屬性。

@property與KVO

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設值方法設值;當須要取值的時候,咱們採用直接訪問實例變量的方式。

另外要注意,初始化方法中,沒有特殊須要,應該儘可能直接訪問實例變量。

@dynamic 動態合成

告訴編譯器不須要自動合成屬性的 setter 和 getter 方法,而由開發之本身動態綁定。 須要注意的是,編譯器不會自動生成 屬性 因此還須要手動定義屬性。

更詳盡的資料這裏有

@property面試資料

參考資料

相關文章
相關標籤/搜索