筆者最近梳理iOS知識(shí)脈絡(luò),計(jì)劃寫(xiě)一個(gè)名為“重識(shí)iOS”的系列,內(nèi)容來(lái)自平時(shí)的學(xué)習(xí)筆記,參考了一些文章和書(shū)籍,融入自己的理解以記錄。歡迎交流指正。本文為第二篇:Property。

property
介紹
- 簡(jiǎn)介:屬性(property)是Objective-C的一項(xiàng)特性,用于封裝對(duì)象中的數(shù)據(jù)。這一特性可以令編譯器自動(dòng)編寫(xiě)與屬性相關(guān)的存取方法,并且保存為各種實(shí)例變量。
- 本質(zhì):屬性的本質(zhì)是實(shí)例變量與存取方法的結(jié)合。
@property = ivar + getter + setter
特質(zhì)
- 原子性:
atomic/nonatomic - 讀寫(xiě)權(quán)限:
readwrite/readonly - 內(nèi)存管理語(yǔ)義:
assign/strong/copy/weak/unsafe_unretained - 方法名:
getter=<name>/setter=<name>
atomic 與 nonatomic
問(wèn)題:什么是原子性? 說(shuō)明并比較atomic和nonatomic。 atomic是百分之百安全的嗎?
- 原子性:并發(fā)編程中確保其操作具備整體性,系統(tǒng)其它部分無(wú)法觀察到中間步驟,只能看到操作前后的結(jié)果。
-
atomic:原子性的,編譯器會(huì)通過(guò)鎖定機(jī)制確保
setter和getter的完整性。 -
nonatomic:非原子性的,不保證
setter和getter的完整性。 -
區(qū)別:由于要保證操作完整,
atomic速度比較慢,線程相對(duì)安全;nonatomic速度比較快,但是線程不安全。atomic也不是絕對(duì)的線程安全,當(dāng)多個(gè)線程同時(shí)調(diào)用setter和getter時(shí),就會(huì)導(dǎo)致獲取的值不一樣。由于鎖定機(jī)制開(kāi)銷較大,一般iOS開(kāi)發(fā)中會(huì)使用nonatomic,而macOS中使用atomic通常不會(huì)有性能瓶頸。 -
拓展:要想線程絕對(duì)安全,就要使用
@synchronized同步鎖。但是由于同步鎖有等待操作,會(huì)降低代碼效率。為了兼顧線程安全和提升效率,可采用GCD并發(fā)隊(duì)列進(jìn)行優(yōu)化改進(jìn)。getter使用同步派發(fā),setter使用異步柵欄。
//同步鎖
- (NSString *)someString {
@synchronized(self) {
return _someString;
}
}
- (void)setSomeString:(NSString *)someString {
@synchronized(self) {
_someString = someString;
}
}
//并發(fā)隊(duì)列
_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
- (NSString *)someString {
__block NSString *localSomeString;
dispatch_sync(_queue, ^{
localSomeString = _someString;
});
return localSomeString;
}
- (void)setSomeString:(NSString *)someString {
dispatch_barrier_async(_queue, ^{
_someString = someString;
});
}
readwrite 與 readonly
讀寫(xiě)權(quán)限不寫(xiě)時(shí)默認(rèn)為 readwrite 。一般可在 .h 里寫(xiě)成 readonly,只對(duì)外提供讀取,在 .m 的 extension中再設(shè)置為 readwrite 可進(jìn)行寫(xiě)入。
//.h文件
#import <Foundation/Foundation.h>
@interface MyClass : NSObject
@property (nonatomic, readonly, copy) NSString *name;
@end
//.m文件
#import "MyClass.h"
@interface MyClass()
@property (nonatomic, readwrite, copy) NSString *name;
@end
內(nèi)存管理語(yǔ)義
關(guān)鍵詞
- strong:表示指向并擁有該對(duì)象。其修飾的對(duì)象引用計(jì)數(shù)會(huì) +1 ,該對(duì)象只要引用計(jì)數(shù)不為 0 就不會(huì)銷毀,強(qiáng)行置空可以銷毀它。一般用于修飾對(duì)象類型、字符串和集合類的可變版本。
-
copy:與
strong類似,設(shè)置方法會(huì)拷貝一份副本。一般用于修飾字符串和集合類的不可變版以及block(歷史遺留問(wèn)題)。 -
weak:表示指向但不擁有該對(duì)象。其修飾的對(duì)象引用計(jì)數(shù)不會(huì)增加,屬性所指的對(duì)象銷毀時(shí)屬性值會(huì)清空。ARC環(huán)境下一般用于修飾可能會(huì)引起循環(huán)引用的對(duì)象,
delegate、xib控件用weak修飾。 -
assign:主要用于修飾基本數(shù)據(jù)類型,如
NSIteger、CGFloat等,這些數(shù)值主要存在于棧中。 -
unsafe_unretained:與
weak類似,但是銷毀時(shí)不自動(dòng)清空,容易形成野指針。
比較 copy 與 strong
- 相同:用于修飾表示擁有關(guān)系的對(duì)象。
- 不同:
strong復(fù)制是多個(gè)指針指向同一個(gè)地址,而copy的復(fù)制是每次會(huì)在內(nèi)存中復(fù)制一份對(duì)象,指針指向不同的地址。NSString、NSArray、NSDictionary等不可變對(duì)象用copy修飾,因?yàn)橛锌赡軅魅胍粋€(gè)可變的版本,此時(shí)能保證屬性值不會(huì)受外界影響。 -
注意:若用
strong修飾NSArray,當(dāng)數(shù)組接收一個(gè)可變數(shù)組,可變數(shù)組若發(fā)生變化,被修飾的屬性數(shù)組也會(huì)發(fā)生變化,也就是說(shuō)屬性值容易被篡改;若用copy修飾NSMutableArray,當(dāng)試圖修改屬性數(shù)組里的值時(shí),程序會(huì)崩潰,因?yàn)閿?shù)組被復(fù)制成了一個(gè)不可變的版本。
比較 assign、weak、unsafe_unretain
- 相同:都不是強(qiáng)引用。
- 不同點(diǎn):
weak引用的對(duì)象被銷毀時(shí), 指針會(huì)被自動(dòng)清空,不再指向銷毀的對(duì)象,不會(huì)產(chǎn)生野指針錯(cuò)誤;unsafe_unretain引用的對(duì)象被銷毀時(shí), 指針并不會(huì)被自動(dòng)清空, 依然指向銷毀的對(duì)象,很容易產(chǎn)生野指針錯(cuò)誤:EXC_BAD_ACCESS;assign修飾基本數(shù)據(jù)類型,內(nèi)存在棧上由系統(tǒng)自動(dòng)回收。
存儲(chǔ)方法名
getter=<name> 與 setter=<name> , <> 中為方法名,通過(guò)此特質(zhì)來(lái)指定存取方法的名稱。
//.h文件
@interface MyClass : NSObject
@property (nonatomic, assign, getter=isOn) BOOL on;
@end
//.m文件
@implementation MyClass
- (BOOL)isOn {
return self.on;
}
@end
默認(rèn)設(shè)置
property有默認(rèn)設(shè)置。
- 基本數(shù)據(jù)類型:
atomic, readwrite, assign - 對(duì)象類型:
atomic, readwrite, strong -
注意:考慮到代碼可讀性以及日常代碼修改頻率,規(guī)范的編碼風(fēng)格中關(guān)鍵詞的順序是:原子性、讀寫(xiě)權(quán)限、內(nèi)存管理語(yǔ)義、
getter/getter。
延伸
我們已經(jīng)知道 @property 會(huì)使編譯器自動(dòng)編寫(xiě)訪問(wèn)這些屬性所需的方法,此過(guò)程在編譯期完成,稱為 自動(dòng)合成 (autosynthesis)。與此相關(guān)的還有兩個(gè)關(guān)鍵詞:@dynamic 和 @synthesize。
- @dynamic:告訴編譯器不要自動(dòng)創(chuàng)建實(shí)現(xiàn)屬性所用的實(shí)例變量,也不要為其創(chuàng)建存取方法。即使編譯器發(fā)現(xiàn)沒(méi)有定義存取方法也不會(huì)報(bào)錯(cuò),運(yùn)行期會(huì)導(dǎo)致崩潰。
-
@synthesize:在類的實(shí)現(xiàn)文件里可以通過(guò)
@synthesize指定實(shí)例變量的名稱。 -
注意:在Xcode4.4之前,
@property配合@synthesize使用,@property負(fù)責(zé)聲明屬性,@synthesize負(fù)責(zé)讓編譯器生成 帶下劃線前綴的實(shí)例變量并且自動(dòng)生成setter、getter方法。Xcode4.4之后@property得到增強(qiáng),直接一并替代了@synthesize的工作。
參考: