一、@property的本質(zhì)
1.本質(zhì)
- 將訪問(wèn)、變量、訪問(wèn)控制進(jìn)行了綁定;編譯器負(fù)責(zé)自動(dòng)合成。
- @property = ivar + getter + setter;
Person : NSObject
@property NSString *firstName;
@property NSString *lastName;
@end
等價(jià)于
Person : NSObject
- (NSString *)firstName;
- (void)setFirstName:(NSString *)firstName;
- (NSString *)lastName;
- (void)setLastName:(NSString *)lastName;
@end
ivar、getter、setter 是如何生成并添加到這個(gè)類(lèi)中的?
- “自動(dòng)合成”( autosynthesis)
完成屬性定義后,編譯器會(huì)自動(dòng)編寫(xiě)訪問(wèn)這些屬性所需的方法,此過(guò)程叫做“自動(dòng)合成”( autosynthesis)。需要強(qiáng)調(diào)的是,這個(gè)過(guò)程由編譯器在編譯期執(zhí)行,所以編輯器里看不到這些“合成方法”(synthesized method)的源代碼。除了生成方法代碼 getter、setter 之外,編譯器還要自動(dòng)向類(lèi)中添加適當(dāng)類(lèi)型的實(shí)例變量,并且在屬性名前面加下劃線(xiàn),以此作為實(shí)例變量的名字。在前例中,會(huì)生成兩個(gè)實(shí)例變量,其名稱(chēng)分別為 _firstName與_lastName。
也可以在類(lèi)的實(shí)現(xiàn)代碼里通過(guò) @synthesize語(yǔ)法來(lái)指定實(shí)例變量的名字.
@implementation Person
@synthesize firstName = _myFirstName;
@synthesize lastName = myLastName;
@end
@synthesize:為屬性創(chuàng)建帶“_”的成員變量,同時(shí)生成getter和setter方法
二、屬性關(guān)鍵字
- 關(guān)鍵字有哪些
atomic、nonatmoic、readonly、readwrite、assign、weak、strong、copy - 關(guān)鍵字間的區(qū)別
nonatomic、atomiac
簡(jiǎn)單從詞意上理解,nonatomic 非原子的, atomiac 原子的 。屬性默認(rèn)是 atomiac , 也就是原子性的。
atomiac: 不受其他線(xiàn)程的影響,在 get 一個(gè)屬性時(shí),立馬給這個(gè)屬性在當(dāng)前線(xiàn)程加一個(gè)鎖,只有當(dāng) get 完成后,才會(huì)解鎖,才會(huì)同步其他線(xiàn)程的 set 值。
nonatomic: 受線(xiàn)程影響,在 get 一個(gè)屬性時(shí), 不管是否有其他線(xiàn)程執(zhí)行 set 方法, 只返回 get 結(jié)束時(shí)的 set 值。
從這也可以看出,nonatomic 聲明的屬性,執(zhí)行速率上是要更快一點(diǎn)的 ; 其實(shí) atomiac 這個(gè)屬性在上層代碼中,其實(shí)非常不常用,因?yàn)楹苌贂?huì)遇到存在同時(shí),多個(gè)線(xiàn)程對(duì)一個(gè)屬性 set 。
readwrite、readonly
詞意上理解,readwrite 讀寫(xiě),readonly 只讀。 屬性默認(rèn)是 readwrite , 支持讀寫(xiě)。
readwirte: 屬性同時(shí)具有 set 和 get 方法。
readonly: 屬性只具有 get 方法。
這倆關(guān)鍵字,是對(duì)set 和 get 方法的一個(gè)總開(kāi)關(guān),就是和其詞意一樣。若只想類(lèi)內(nèi)部 set , 就聲明 readonly。
- strong、retain、weak、assign、copy、unsafe_unretained
這個(gè)幾個(gè)從詞意上就很難理解了。 retain 、assign 是 MRC 時(shí)的關(guān)鍵字,到 ARC 時(shí),換成了 strong 和 weak 。 屬性默認(rèn)是 MRC -- assign ;ARC -- object 是 strong,基本數(shù)據(jù)類(lèi)型還是 assign 。 實(shí)際上 weak 和 assign 還是有一些不同的,strong 和 retain 幾乎沒(méi)什么區(qū)別,不過(guò)建議還是能用 retain 的地方盡量用 strong , 后面也不講 retain 。 講到這幾個(gè)關(guān)鍵字,就必須說(shuō)到引用計(jì)數(shù)(retainCount)和生命周期。
Use Strong and Weak Declarations to Manage Ownership
By default, object properties declared like this:
@property id delegate;
use strong references for their synthesized instance variables. To declare a weak reference, add an attribute to the property, like this:
@property (weak) id delegate;
Note: The opposite to weak is strong. There’s no need to specify the **strong** attribute explicitly, because it is the **default**.
這里提一下,iOS系統(tǒng)中,默認(rèn)的屬性:ARC 中 對(duì)象默認(rèn)是 strong,基本數(shù)據(jù)類(lèi)型默認(rèn)還是 assign。
對(duì)整個(gè) APP 來(lái)說(shuō)是內(nèi)存管理機(jī)制,對(duì)單個(gè)屬性來(lái)說(shuō)就是生命周期 ,而引用計(jì)數(shù)就是核心。
這里簡(jiǎn)單說(shuō)明一下: iOS 有個(gè)內(nèi)存池的概念,所有的屬性創(chuàng)建的時(shí)候都會(huì)被內(nèi)存池關(guān)注,這個(gè)時(shí)候 retainCount = 1 ,中間對(duì)這個(gè)屬性進(jìn)行操作時(shí), retainCount 可能會(huì)增加 或者 減少 ,但當(dāng) retainCount = 0 時(shí), 內(nèi)存池檢測(cè)到后,就會(huì)釋放這個(gè)屬性對(duì)應(yīng)的內(nèi)存空間 。
strong、weak、assign、unsafe_unretained
- strong 和 weak
strong 是每對(duì)這個(gè)屬性引用一次,retainCount 就會(huì)+1,只能修飾 NSObject 對(duì)象,不能修飾基本數(shù)據(jù)類(lèi)型。是 id 和 對(duì)象 的默認(rèn)修飾符。
weak 對(duì)屬性引用時(shí),retainCount 不變,只能修飾 NSObject 對(duì)象,不能修飾基本數(shù)據(jù)類(lèi)型。 主要用于避免循環(huán)引用。 - assign
這個(gè)關(guān)鍵字,可以修飾基本數(shù)據(jù)類(lèi)型和 NSObject 對(duì)象。
對(duì)這個(gè)關(guān)鍵字聲明的屬性操作時(shí),retainCount 是一直不變的,一直為 1,只有主動(dòng)調(diào)用 release 時(shí) ,才會(huì)釋放。
但是為什么我們不會(huì)用assign去聲明對(duì)象呢?
這是因?yàn)?assign 修飾的對(duì)象(一般編譯的時(shí)候會(huì)產(chǎn)生警告:Assigning retained object to unsafe property; object will be released after assignment)在釋放之后,指針的地址還是存在的,也就是說(shuō)指針并沒(méi)有被置為nil,造成野指針。對(duì)象分配在堆上的某塊內(nèi)存,如果在后續(xù)的內(nèi)存分配中,剛好分到了這塊地址,程序就會(huì) crash。
為什么可以用assign修飾基本數(shù)據(jù)類(lèi)型?
因?yàn)榛A(chǔ)數(shù)據(jù)類(lèi)型是分配在棧上,棧的內(nèi)存會(huì)由系統(tǒng)自己自動(dòng)處理回收,不會(huì)造成野指針。
- unsafe_unretained
這個(gè)關(guān)鍵字和 week 非常相似, 也是可以同時(shí)修飾基本數(shù)據(jù)類(lèi)型和 NSObject 對(duì)象 ,其實(shí)它本身是 week 的前身 , 在 iOS5 之后,基本都用 week 代替了 unsafe_unretained 。 但它們之間還是稍微有點(diǎn)區(qū)別的,并不是完全一樣,對(duì)上層代碼來(lái)說(shuō),能用 unsafe_unretained 的地方,都可以用 week 代替。同時(shí)要注意一點(diǎn),這個(gè)修飾符修飾的變量不屬于編譯器的內(nèi)存管理對(duì)象。 - copy
復(fù)制的意思,意思非常明確,但用起來(lái)是最要注意的。
這個(gè)關(guān)鍵字類(lèi)似 strong ,只能修飾 NSObject 對(duì)象,不能修飾基本數(shù)據(jù)類(lèi)型。和 strong 不一樣的地方是, copy 后的對(duì)象 ,指針地址是和之前不一樣的,也就是說(shuō)重新分配了一塊內(nèi)存,也就是所謂的深拷貝。這個(gè)關(guān)鍵字在用的時(shí)候,因?yàn)樯婕暗缴暾?qǐng)新的內(nèi)存空間,所以要少用,能用 strong 的地方都用 strong ,只有必須用 copy 的地方才用 copy 。
另外要注意,copy 修飾可變類(lèi)型的屬性時(shí)要小心,如NSMutableArray、
三、@synthesize 和 @dynamic 分別有什么作用?
- @property 有兩個(gè)對(duì)應(yīng)的詞,一個(gè)是 @synthesize,一個(gè)是 @dynamic。如果 @synthesize 和 @dynamic 都沒(méi)寫(xiě),那么默認(rèn)的就是 @syntheszie var = _var;
- @synthesize 的語(yǔ)義是如果你沒(méi)有手動(dòng)實(shí)現(xiàn) setter 方法和 getter 方法,那么編譯器會(huì)自動(dòng)為你加上這兩個(gè)方法。
- @dynamic 告訴編譯器:屬性的 setter 與 getter 方法由用戶(hù)自己實(shí)現(xiàn),不自動(dòng)生成。(當(dāng)然對(duì)于 readonly 的屬性只需提供 getter 即可)。
假如一個(gè)屬性被聲明為 @dynamic var,然后你沒(méi)有提供 @setter 方法和 @getter 方法,編譯的時(shí)候沒(méi)問(wèn)題,但是當(dāng)程序運(yùn)行到 instance.var = someVar,由于缺 setter 方法會(huì)導(dǎo)致程序崩潰;或者當(dāng)運(yùn)行到 someVar = var 時(shí),由于缺 getter 方法同樣會(huì)導(dǎo)致崩潰。編譯時(shí)沒(méi)問(wèn)題,運(yùn)行時(shí)才執(zhí)行相應(yīng)的方法,這就是所謂的動(dòng)態(tài)綁定。