@property 的屬性關(guān)鍵字有 nonatomic
、atomic
、readonly
纷铣、writeonly
倔喂、readwrite
咆霜、assign
注益、retain
舶沿、copy
、strong
配并、weak
括荡、unsafe_unretained
、nonnull
溉旋、nullable
畸冲、null_resettable
。
下面介紹一些常用的關(guān)鍵字:
1.) nonatomic
和 atomic
:
不寫的話默認(rèn)就是 atomic 观腊,默認(rèn)關(guān)鍵字邑闲。atomic
和 nonatomic
的區(qū)別在于,系統(tǒng)自動生成的 getter/setter 方法不一樣梧油。如果你自己寫 getter/setter苫耸,那 atomic/nonatomic/retain/assign/copy 這些關(guān)鍵字只起提示作用,寫不寫都一樣婶溯。
對于atomic
的屬性鲸阔,系統(tǒng)生成的 getter/setter 會保證 get偷霉、set 操作的完整性迄委,不受其他線程影響。假設(shè)有一個atomic
的屬性"name"
类少,如果線程 A 調(diào)[self setName:@"A"]
叙身,線程 B 調(diào)[self setName:@"B"]
,線程 C 調(diào)[self name]
硫狞,那么所有這些不同線程上的操作都將依次順序執(zhí)行——也就是說信轿,如果一個線程正在執(zhí)行 getter/setter,其他線程就得等待残吩。因此财忽,屬性 name 是讀/寫安全的。
但是泣侮,如果有另一個線程 D 同時在調(diào)[name release]
即彪,那可能就會crash,因為 release 不受 getter/setter 操作的限制活尊。也就是說隶校,這個屬性只能說是讀/寫安全的,但并不是線程安全的蛹锰,因為別的線程還能進(jìn)行讀寫之外的其他操作深胳。線程安全需要開發(fā)者自己來保證。
如果 name
屬性是 nonatomic
的铜犬,那么上面例子里的所有線程 A舞终、B轻庆、C、D 都可以同時執(zhí)行敛劝,可能導(dǎo)致無法預(yù)料的結(jié)果榨了。如果是 atomic
的,那么 A攘蔽、B龙屉、C 會串行,而 D 還是并行的满俗。
2.)readonly
只讀屬性转捕,有g(shù)etter方法,但是直接調(diào)用setter方法會報錯唆垃。
但是我們還是可以利用KVC
為readonly
屬性賦值五芝。
例如:定義一個ACLStudent
的類,該類里面有一個firstName
屬性。
如果直接調(diào)用setter方法辕万,會報錯
改用KVC來賦值枢步,就可以修改成功
當(dāng)使用 setValue:forKey:
來設(shè)置對象的屬性時,會以下面的優(yōu)先順序來尋找對應(yīng)的 key
:
- 消息接收對象會查找是否存在滿足
set<Key>:
格式的存取方法渐尿。 - 如果不存在滿足條件的存取方法醉途,且消息接收對象的類方法
+ (BOOL)accessInstanceVariablesDirectly
返回 YES,那么該對象會以_<key>
,_is<Key>
,<key>
,is<Key>
的順序查找是否存在對應(yīng)的key砖茸。 - 如果存在對應(yīng)的存取方法或者找到對應(yīng)的實例變量隘擎,那么就會改變該 key 所對應(yīng)的值 value。必要的話凉夯,value 所對應(yīng)的值會從對象中解析出來货葬,如 Representing Non-Object Values 所描述的那樣。
- 如果沒有找到對應(yīng)的存取方法或者實例變量劲够,那么該消息對象的
setValue:forUndefinedKey:
將會調(diào)用震桶。
對于上述第2點說明一下,如果我們不想讓 setValue:forKey:
方法改變對象的屬性值征绎,那么重寫其類方法 + (BOOL)accessInstanceVariablesDirectly
返回 NO (該方法默認(rèn)返回 YES蹲姐,即在不存在滿足條件的存取方法時,允許直接訪問屬性對應(yīng)的實例變量)炒瘸;在搜索實例變量時淤堵,會首先檢查帶下劃線的實例變量,然后檢查不帶下劃線的實例變量顷扩。
對于上述第3點舉例說明拐邪,如果要修改對象的屬性 NSInteger studentId
, 注意其是 NSInteger
類型,我們在調(diào)用 setValue:forKey:
方法時可以像這樣
[self setValue:@(20) forKey:NSStringFromSelector(@selector(studentId))];
傳入一個 NSNumber 對象也可以隘截,Objective-C 會處理好一切扎阶。
對于上面的示例汹胃,使用 setValue:forKey:
實際修改的是 _firstName
實例變量的值。不要忘記东臀,我們在聲明一個 firstName
的屬性時着饥,編譯器會為我們自動合成一個_firstName
的實例變量。
總結(jié):
當(dāng)我們聲明一個 readonly 的屬性惰赋,外部可能會通過 KVC 修改該屬性值宰掉。
為了避免 KVC 修改屬性值,須將定義屬性所在類的類方法 + (BOOL)accessInstanceVariablesDirectly
重寫赁濒,使其返回 NO.
3.)strong
和 weak
strong
: 持有對象轨奄,引用計數(shù)+1;
weak
: 不持有對象拒炎,引用計數(shù)不變挪拟,只適用于對象。比如代理(為了防止循環(huán)引用)击你,UI控件(add到父視圖上時玉组,被父視圖持有。對UI控件的引用丁侄,iOS會自動將其設(shè)置為弱變量(weak))
不同的是, 當(dāng)一個對象不再有strong
類型的指針指向它的時候 它會被釋放 惯雳,即使還有weak
型指針指向它。
一旦最后一個strong
型指針離去 绒障,這個對象將被釋放吨凑,所有剩余的weak
型指針都將被清除。
當(dāng)weak
修飾的對象被別的變量釋放户辱,那么弱變量會被自動設(shè)置為nil,這樣可以有效地防止崩潰
4.) retain
糙臼、 assign
庐镐、 unsafe_unretained
assign
: 修飾基礎(chǔ)數(shù)據(jù)類型和C數(shù)據(jù)類型,當(dāng)被釋放后变逃,變量不會被自動置為nil必逆,會變成野指針。
unsafe_unretained
:unsafe_unretained
從命名就可以看出意義所在揽乱,unsafe即是不會自動設(shè)置為nil名眉,如果對象被釋放了,再進(jìn)行訪問凰棉,程序會crash损拢;unretained
與weak
類似,不會影響對象的引用計數(shù)
retain
:釋放舊對象撒犀,創(chuàng)建新對象福压。 引用計數(shù)會+1掏秩,強(qiáng)引用。
copy
: 拷貝一個新的對象荆姆,新對象的引用計數(shù)+1蒙幻,釋放舊對象。
5.) copy
首先我們看一下對象和指針胆筒,看圖
中間這個 “=” 符號 將2段代碼連在一起邮破, 充當(dāng)一個橋梁作用:
第一: 允許 p 指針 指向這個對象占用的內(nèi)存區(qū)域的首地址(可以簡潔的理解為 允許p指針指向?qū)ο螅?/p>
第二: 將這個對象地址賦值給p指針
第三:后續(xù)所有對 “對象“的操作,都可以通過對p指針進(jìn)行間接操作來完成仆救。
copy
和 mutableCopy
copy
就是淺拷貝决乎,mutableCopy
就是深拷貝嗎?
任何對象都可以執(zhí)行 copy
和 mutableCopy
派桩?
看一下官方關(guān)于深拷貝和淺拷貝的解釋圖:
結(jié)論:
1.淺復(fù)制构诚,復(fù)制的是指向?qū)ο蟮闹羔槪⒉粫?fù)制對象本身铆惑,不會創(chuàng)建一個新的對象范嘱;
2.,深復(fù)制员魏,復(fù)制的是對象本身丑蛤,會創(chuàng)建一個新的對象。
對引用計數(shù)的影響:
淺copy撕阎,類似strong受裹,持有原始對象的指針,會使retainCount加一虏束。淺copy和strong引用的區(qū)別僅僅是淺copy多執(zhí)行一步copyWithZone:方法棉饶。
深copy,會創(chuàng)建一個新的對象镇匀,不會對原始對象的retainCount變化照藻。
使用原則:
\ | copy |
mutableCopy |
---|---|---|
不可變對象 |
原來的對象,不可變 |
新對象 ,可變 |
可變對象 |
新對象汗侵,不可變 |
新對象幸缕,可變 |
1.針對不可變對象調(diào)用copy返回該對象本身,調(diào)用mutableCopy返回一個可變對象(新的)晰韵;
2.針對可變對象調(diào)用copy返回一個不可變對象(新的)发乔,調(diào)用mutableCopy返回另外一個可變對象(新的)。
3.針對集合類對象(NSArray,NSMutableArray,NSDictionary,NSMutableDictionary,NSSet等)進(jìn)行深拷貝雪猪,拷貝的是對象本身栏尚,變成一個新的對象,但是集合里面的元素浪蹂,進(jìn)行的是淺拷貝抵栈,拷貝的是地址告材。
注意:
使用copy
,需要該對象遵循NSCopying
協(xié)議;
使用mutableCopy
,需要該對象遵循NSMutableCopying
協(xié)議古劲,否則會crash在copyWithZone
和mutableCopyWithZone
方法上斥赋。
例:
UIView及其父類 并沒有像 數(shù)組 字典 字符串這些類一樣 遵守 NSCopying, NSMutableCopying 協(xié)議,下面代碼程序會crash产艾。
@property(nonatomic, copy)NSMutableArray *myCopyArray;
self.myCopyArray = [NSMutableArray arrayWithObjects:@"1",@"2",@"3", nil];
[self.myCopyArray removeObjectAtIndex:0];
運(yùn)行后crash:
-[__NSArrayI removeObjectAtIndex:]: unrecognized selector sent to instance 0x14fc18f20
所以使用的時候疤剑,要注意對象是可變的還是不可變的。
關(guān)于 copy 和 block
: