2016.5.30更新:
經(jīng)評論區(qū)提醒:
其實還有兩個特性:為空性nullability
和自定義getter
,setter
方法名屬性。
這兩個特性比較簡單也不太常用,所以就不納入文章。
2016.6.3更新:
今天在《iOS編程》書中看到,block對象的屬性聲明應(yīng)該為copy
(本文之前的觀點(diǎn)是weak
)更為準(zhǔn)確。因為Block
對象是在棧中創(chuàng)建的,而其他對象是在堆中創(chuàng)建的。這意味著,即使應(yīng)用針對新創(chuàng)建的Block
對象保留了強(qiáng)引用類型的指針,一旦創(chuàng)建該對象的方法返回,那么與方法內(nèi)部的其他局部變量相同,新創(chuàng)建的Block
對象也會被立即釋放。為了在聲明Block
對象的方法返回后仍然保留該對象,必須向其發(fā)送copy
消息。拷貝某個Block
對象時,應(yīng)用會在堆中創(chuàng)建該對象的備份。這樣,即使應(yīng)用釋放了當(dāng)前方法的棧,堆中的Block
對象也不會被釋放。
2016.6.3更新:
本文之前指出IBOutlet
屬性應(yīng)該設(shè)為weak
,但是在WWDC2015上Apple官方推薦IBOutlet
屬性應(yīng)該設(shè)為strong
,除非需要避免引用循環(huán)的屬性才設(shè)置為weak
。在stackoverflow上有關(guān)于這個問題的討論,我覺得最佳實踐應(yīng)該是頂層視圖的IBOutlet
屬性使用strong
,子視圖的使用weak
。(《iOS編程》中也是這個觀點(diǎn))
詳見:http://stackoverflow.com/questions/7678469/should-iboutlets-be-strong-or-weak-under-arc
最近在找實習(xí)工作,幾乎每次面試都會被問及@property
后的三個關(guān)鍵字。網(wǎng)上不是說面試一個人的iOS開發(fā)水平,問個property就大概知道了。所以今天花了一些時間總結(jié)了一下,這些內(nèi)容都來自于我看過的書以及在網(wǎng)上查閱的一些資料。
在iOS開發(fā)中,任何一個屬性都有三個特性(@property
后面可以跟三個關(guān)鍵字),每個特性都有多種不同的可選類型。
多線程特性
默認(rèn):atomic
atomic
:原子的。表示線程安全。使用atomic
的目的是為了確保其他線程不在同一時間內(nèi)訪問相同的資源。(編譯器會自動生成互斥加鎖的代碼,避免變量的讀寫不同步)但往往即使聲明了atomic
屬性也不能一定保證線程安全,而且這種機(jī)制是耗費(fèi)系統(tǒng)資源的。(所以一般都聲明為nonatomic
屬性)nonatomic
:非原子的。表示非線程安全。可以在不同的地方讀取和設(shè)置屬性的值。(可能會導(dǎo)致讀寫不同步)編譯器會少生成一些互斥加鎖的代碼,可以提高效率。
總結(jié):涉及到多線程的時候,使用atomic
,保證安全。不涉及多線程,使用nonatomic
,效率更高。
原子操作:是指不會被線程調(diào)度機(jī)制打斷的操作。原子操作一旦開始,就要一直運(yùn)行到結(jié)束,不會被打斷。
讀寫特性
默認(rèn):readwrite
-
readwrite
:編譯器會為屬性生成get方法和set方法 -
readonly
:編譯器只生成get方法
readonly
一般用于設(shè)置內(nèi)部數(shù)據(jù)的訪問權(quán)限:某個對象中有一種可修改的數(shù)據(jù),但是除該對象外,其他數(shù)據(jù)只能訪問該數(shù)據(jù)而不能修改它。這時我們就可以為該數(shù)據(jù)另外設(shè)置一個readonly
屬性僅供外界讀取,修改則在該對象中修改readwrite
屬性的數(shù)據(jù)。(這也是一種常用的設(shè)計模式)
內(nèi)存管理特性(我對ARC的理解)
默認(rèn):strong
iOS5后使用ARC來管理內(nèi)存。ARC的原則:只要某個對象被任一strong
指針指向,那么他將不會被銷毀。當(dāng)對象沒有被任何strong
指針指向,那么該對象將被銷毀。
strong
:使用strong
屬性會引起引用計數(shù)加1。是指針拷貝(淺拷貝),不會拷貝內(nèi)容。當(dāng)有某個strong
指針指向某個對象時,該對象不會被銷毀,只有當(dāng)strong
指針設(shè)定了新的值,或是超出了作用范圍時,該strong
指針就不再持有該對象,倘若該對象不被其他strong
指針持有,該對象就會被釋放。weak
:表示一種“非擁有關(guān)系”。為這種屬性設(shè)置新值時,設(shè)置方法既不釋放舊值,也不保留新值,不會使引用計數(shù)加1。當(dāng)所指對象被銷毀時,指針會自動被置為nil
,防止野指針。
【適用范圍:delegate,IBOutlet屬性】
weak
指針還可以解決強(qiáng)引用循環(huán)(strong reference cycle/retain cycle):當(dāng)兩個或兩個以上對象之間互相強(qiáng)引用時,無法通過ARC來釋放對象,可能會導(dǎo)致內(nèi)存泄漏。解決辦法是將其中一個指針改為weak
。具體改哪一個,可以為存在強(qiáng)引用循環(huán)的對象決定父子關(guān)系。父對象應(yīng)該使用具有強(qiáng)引用特性的指針指向子對象,子對象應(yīng)該使用具有弱引用特性的指針指向父對象。copy
:先copy
一個相同對象,再創(chuàng)建一個strong
指針。(深拷貝,會拷貝內(nèi)容)
【當(dāng)某對象的類具有可修改的子類時,應(yīng)該將屬性設(shè)為copy
。例如:NSString
,NSArray
,NSDictionary
】
這樣做的原因是:如果屬性指向的對象的類具有可修改的子類,那個該屬性可能會指向可修改的子類對象,同時該子類對象可能會被其他擁有者修改。因此,最好先復(fù)制該對象,然后再將屬性指向復(fù)制后的對象。(編寫具有“防御性”的代碼)
@property (nonatomic, copy) NSString *string_1;
@property (nonatomic, strong) NSMutableString *string_2;
self.string_2 = [[NSMutableString alloc] init];
self.string_1 = self.string_2;
上述代碼中string_2
可能會被改變,但是string_1
是不可變類型的。】
擴(kuò)展:這個寫法會出什么問題:
@property (nonatomic,copy) NSMutableArray *array;
添加,刪除,修改數(shù)組內(nèi)的元素的時候,程序會因為找不到對應(yīng)的方法(unrecognised selector
)而崩潰.因為 copy
就是復(fù)制一個不可變 NSArray
的對象。
-
unsafe_unretained
:(不安全不引用)用于非對象屬性(即:基本數(shù)據(jù)類型),這類屬性不需要做內(nèi)存管理,它表示存取方法會直接為實例變量賦值。【MRC時期使用assign
】
【unsafe是相對于weak
而言的。unsafed_unretained
類型的指針指向的對象被銷毀時,指針不會自動設(shè)置為nil
,而是成為空指針,因此不安全。但是當(dāng)處理非對象屬性時是不會出現(xiàn)空指針問題的】
【unretained是指不會引起引用計數(shù)加1】
補(bǔ)充:
MRC時期的關(guān)鍵字:
-
assign
(賦值):表示簡單的直接賦值操作。- 用于基本數(shù)據(jù)類型(
NSInteger
,CGFloat
等)和C數(shù)據(jù)類型(int
,float
,double
等) - 用于id類型。(比如delegate屬性,使用
weak
可以避免出現(xiàn)強(qiáng)引用循環(huán))【當(dāng)id類型使用assign
時,對象被銷毀,指針不會被置空,可能會引起空指針】
在引入ARC后,assign
的第一個功能已經(jīng)被unsafed_unretained
取代,第二個功能被weak
取代
- 用于基本數(shù)據(jù)類型(
-
retain
(持有):先release
原來的值,再retain
新值(引用計數(shù)會自動加1)。
-(void)setA:(ClassA *)a{
if(_a!=a){
[_a release];
_a=[a retain];
}
}
在引入ARC后,使用strong
代替retain
當(dāng)然以上只是我目前的理解,我相信以后肯定會有更深的理解。所以我會隨時更新我的新看法的。