關(guān)于“屬性”
屬性的作用域:
很多語言在定義屬性時(shí)可以指定其作用域(private/public等),OC中也有該關(guān)鍵字(@private/@public),不過幾乎不會(huì)用到。因?yàn)檫@種寫法導(dǎo)致的結(jié)果:對象布局在編譯期就已經(jīng)確定了。這有什么問題嗎逸寓?
嗯,是有問題的。
對象的屬性在內(nèi)存中被訪問是通過其和對象在內(nèi)存中的偏移量來查找的康吵。這種寫法的問題是當(dāng)你給對象新加入一個(gè)屬性后,原來各屬性的偏移量會(huì)弄錯(cuò)亂访递,所以需要重新編譯晦嵌。例如,某代碼庫中的一個(gè)類使用的是舊的類定義拷姿,而和它進(jìn)行鏈接的類卻新定義了屬性惭载,那么就會(huì)出現(xiàn)不兼容現(xiàn)象。
OC解決這種問題的方案是在運(yùn)行期才進(jìn)行判斷响巢,若發(fā)現(xiàn)類的定義變了描滔,則按照最新的(此時(shí)已編譯過)偏移量去查找屬性。這是極好極安全的踪古。
由屬性自動(dòng)合成存取方法(setter/getter):
@property (nonatomic, copy)NSString *userName;
@property會(huì)自動(dòng)在我們的屬性名前加下劃線含长,所以真正的屬性名是_userName券腔。
@synthesize userName = _myUserName;
用于自定義屬性名,此時(shí)屬性名是_myUserName;(不過一般情況下貌似沒有自定義屬性名的這個(gè)必要)拘泞。
最重要的是編譯器會(huì)幫我們生成屬性存取方法纷纫。而你若想阻止編譯器自動(dòng)合成setter和getter方法,則可以使用@dynamic userName;
關(guān)鍵字陪腌。需要注意的是@synthesize和@ dynamic關(guān)鍵字都是寫在.m文件的@implementation下的辱魁。
修飾屬性的一些關(guān)鍵字
- 原子性:所謂原子性,就是說在操作該屬性時(shí)會(huì)加鎖诗鸭,確保讀寫正確染簇。atomic代表該屬性是原子性的,而nonatomic則相反强岸,代表非原子性的锻弓。一般情況下我們都使用nonatomic,因?yàn)樵有缘拇鷥r(jià)是性能消耗厲害蝌箍。
- 讀寫權(quán)限:readonly代表只可讀弥咪,writeonly代表只可寫。
- 內(nèi)存管理:在這里我只說copy十绑,因?yàn)橐郧皩opy沒有深入理解過聚至。
關(guān)于為什么NSString類型的屬性要用copy
注意:我們定義屬性時(shí)在@property后加了這些修飾詞后,編譯器會(huì)幫我們自動(dòng)合成滿足這些條件的存取方法:
- (void)setUserName:(NSString *)userName
{
if(_userName!=userName)
{
_userName = [userName copy];
}
}
同理本橙,我們自定義初始化方法時(shí)也要遵守我們定義屬性時(shí)的語義扳躬。既然屬性_userName是NSString類型的,是被copy修飾的甚亭,那我們定義初始化方法時(shí)就應(yīng)該這樣:
- (id)initWithUserName:(NSString *)userName
{
self = [super init];
if(self)
{
_userName = [userName copy];
}
return self;
}
使用_userName還是self.userName贷币?
在對象內(nèi)部是直接訪問實(shí)例變量呢(_userName)?還是通過存取方法訪問呢(setter/getter)亏狰?
直接訪問_userName當(dāng)然速度更快役纹。
但是繞過了存取方法,我們定義屬性時(shí)的修飾語義也就不會(huì)落實(shí)暇唾。比如促脉,沒有了給_userName屬性賦值時(shí)的copy處理,則不太安全策州。
而且直接訪問_userName不會(huì)觸發(fā)鍵值觀察(KVO),這個(gè)具體情況做取舍瘸味。
所以,綜上所述够挂,我們折中的方案是:在獲取屬性值時(shí)直接讀取實(shí)例變量_userName旁仿;而在賦值時(shí)要通過setter方法。
NSLog(@"%@",_userName); // 讀取
self.userName = @"wang66"; // 賦值