實(shí)例變量:
屬性其實(shí)說(shuō)直白點(diǎn)就是 ivar + setter + getter(實(shí)例變量+存取方法),不過(guò)在OC中屬性多了字面量這一系列特殊關(guān)鍵字使得OC屬性有些不同。
成員屬性我們應(yīng)該都使用過(guò),比如現(xiàn)在定義一個(gè)Car類(lèi)有name和speed成員變量:
#import <Foundation/Foundation.h>@interface Car : NSObject{ @public NSString *name; NSInteger speed;}@end
在OC類(lèi)的內(nèi)部有一個(gè)偏移量,專(zhuān)門(mén)標(biāo)記成員變量在內(nèi)存中的所在位置。如果現(xiàn)在在添加一個(gè)新的成員變量在name的前面,那么就會(huì)出現(xiàn)偏移量整體便宜的問(wèn)題,現(xiàn)在添加一個(gè)PRice實(shí)例:
#import <Foundation/Foundation.h>@interface Car : NSObject{ @public NSInteger price; NSString *name; NSInteger speed;}@end
此時(shí)偏移量在內(nèi)存中顯示如下:
| Car | Car |
| name | price |
| speed | name |
| speed |
可以看到實(shí)例偏移量發(fā)生了改變,但是OC將實(shí)例變量作為一種存儲(chǔ)偏移量所用的“特殊變量”,交個(gè)類(lèi)對(duì)象(class object)保管,偏移量會(huì)在運(yùn)行時(shí)查找,所以總能正確的找到偏移量。
@Property
使用屬性相比成員變量更加抽象,能夠使用setter和getter對(duì)變量做更多的處理。
說(shuō)一下屬性的特性
@synthesize關(guān)鍵字
該關(guān)鍵字指定了屬性的實(shí)例變量名稱,并且根據(jù)存儲(chǔ)語(yǔ)義(readwrite、readonly)系統(tǒng)自動(dòng)合成setter和getter方法,當(dāng)然也可以手寫(xiě)來(lái)覆蓋系統(tǒng)提供的。
@dynamic
該關(guān)鍵字告訴編譯器不要為我合成setter和getter方法,這些方法將由我自己實(shí)現(xiàn)。當(dāng)然我們可以不實(shí)現(xiàn)這在編譯階段不會(huì)出現(xiàn)問(wèn)題,直到運(yùn)行時(shí)才會(huì)檢查是否實(shí)現(xiàn)了setter和getter,如果沒(méi)有實(shí)現(xiàn)就會(huì)拋出異常。
例如在CoreData中NSManagedObject子類(lèi)的所有屬性全部都是dynamic標(biāo)記的,這是因?yàn)樽宇?lèi)的某些屬性不是真正的實(shí)例變量,而是對(duì)應(yīng)背后的數(shù)據(jù)庫(kù),對(duì)NSManagedObject對(duì)象通過(guò)是屬性訪問(wèn)時(shí)會(huì)自動(dòng)使用KVC。
屬性特性(語(yǔ)義)
屬性的特質(zhì)分為四類(lèi):
1.原子性:
原子性就是指該屬性是否為同步的,OC中大部分屬性都是nonatomic(非原子性)的,如果不寫(xiě)nonatomic那么就會(huì)是原子性的。理論上來(lái)說(shuō)原子性屬性的讀寫(xiě)都將會(huì)是同步的,但是OC中atomic并不能一定確定屬性為同步的,如果真要進(jìn)行同步操作,還要用更加深層次的同步鎖API。而且atomic會(huì)很影響效率,所以一般都會(huì)寫(xiě)nonatomic。
2.讀/寫(xiě)權(quán)限:
讀寫(xiě)為readonly和readwrite兩種,前一種在系統(tǒng)只會(huì)合成getter方法,而后一種則會(huì)同時(shí)生成setter和getter。如果屬性設(shè)置為了readonly屬性,那么該屬性是不可以修改的。
3.內(nèi)存管理語(yǔ)義:
assign:該方法只會(huì)針對(duì)“純量類(lèi)型”(CGFloat或NSInteger等)的簡(jiǎn)單賦值操作,id類(lèi)型也要用assign,所以一般iOS中的代理delegate屬性都會(huì)用assign來(lái)標(biāo)示,如:
@property (nonatomic, assign) id <UITableViewDataSource> dataSource;@property (nonatomic, assign) id <UITableViewDelegate> delegate;
strong: 使用該特性實(shí)例變量在賦值時(shí),會(huì)釋放舊值同時(shí)設(shè)置新值,對(duì)對(duì)象產(chǎn)生一個(gè)強(qiáng)引用,用MRC來(lái)說(shuō)就是引用計(jì)數(shù)+1。
weak: 屬性表明了一種”非擁有關(guān)系“,既不釋放舊值,也不保留新值。用MRC就是引用計(jì)數(shù)不變,當(dāng)指向的對(duì)象被釋放時(shí),該屬性自動(dòng)被設(shè)置為nil。這里多說(shuō)一點(diǎn),weak的runtime實(shí)現(xiàn)是通過(guò)hash表完成的,用變量名做鍵,一旦發(fā)現(xiàn)屬性所指的對(duì)象被釋放了,立刻設(shè)置為nil。
unsafe_unretained:和weak一樣,唯一的區(qū)別就是當(dāng)對(duì)象被釋放后,該屬性不會(huì)被設(shè)置為nil。所以是unsafe的。
copy:和strong類(lèi)似,不過(guò)該屬性會(huì)被復(fù)制一個(gè)新的副本。很多時(shí)使用copy是為了方式Mutable(可變類(lèi)型)在我們不知道的情況下修改了屬性值,而用copy可以生成一個(gè)不可變的副本防止被修改。如果我們自己實(shí)現(xiàn)setter方法的話,需要手動(dòng)copy。
4.方法名:
getter = <name>
setter = <name>
方法名可以修改為我們合成的方法名,可以使存取方法語(yǔ)義更加符合應(yīng)用場(chǎng)景。
如果要在其它屬性里面設(shè)置屬性的話,還是要符合屬性特性,比如copy的話我們還是要手動(dòng)copy一下屬性。這里說(shuō)一下構(gòu)造方法里需要直接操作實(shí)例變量,而不應(yīng)該調(diào)用setter和getter。
對(duì)象內(nèi)部盡量直接訪問(wèn)實(shí)例變量
首先說(shuō)一下構(gòu)造方法和析構(gòu)方法中為什么不能使用setter和getter,因?yàn)閟etter和getter是經(jīng)過(guò)我們包裝過(guò)的方法,有可能增加一些判斷,而如果子類(lèi)調(diào)用父類(lèi)的構(gòu)造方法同時(shí)實(shí)現(xiàn)了自己的setter和getter,那么很可能就會(huì)出現(xiàn)問(wèn)題。
通過(guò)屬性訪問(wèn)實(shí)例變量會(huì)使用屬性的字面語(yǔ)義,會(huì)使用KVO所以在執(zhí)行效率上肯定比直接調(diào)用實(shí)例變量慢,但是通過(guò)屬性訪問(wèn)可以截獲屬性的獲取和設(shè)置更加方便調(diào)試和控制。
一般在類(lèi)內(nèi)部推薦設(shè)置用setter 獲取直接用實(shí)例變量。
這里再說(shuō)一下惰性加載,所謂惰性加載就是指,屬性會(huì)在第一次調(diào)用getter的時(shí)候初始化,如下:
-(NSString *)name{ if (!_name){ _name = [[NSString alloc] init]; } return _name;}
那么此時(shí)就只能夠通過(guò)getter來(lái)調(diào)用實(shí)例變量了。
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注