“屬性”(property)是OC的一項(xiàng)特性,用于封裝對(duì)象中的數(shù)據(jù)。
@Property是聲明屬性的語(yǔ)法(@property = ivar + getter + setter
)。
OC對(duì)象通常會(huì)把其所需的數(shù)據(jù)保存為各種實(shí)例變量(ivar
)。實(shí)例變量一般通過(guò)“存取方法”(accessmethod
)來(lái)訪問(wèn)。
什么是存取方法:
getter
和setter
方法(access method = getter + setter
),其中getter
用于獲取變量value
, 而setter
用于寫(xiě)入value
。
@Property
可以快速方便的為實(shí)例變量創(chuàng)建存取器。
// Man.h#import <Foundation/Foundation.h>@interface Man : NSObject@property (nonatomic,strong)NSString *name;@property (nonatomic,strong)NSString *sex;@end
與下面的寫(xiě)法等效
// Man.h#import <Foundation/Foundation.h>@interface Man : NSObject{ // 實(shí)例變量 NSString *name; NSString *sex;}// setter- (void)setName:(NSString *)newName;// getter- (NSString *)name;// setter- (void)setSex:(NSString *)newSex;// getter- (NSString *)sex;@end
access method = getter + setter
)。self. name = @"sky";NSString *name = self. name;
點(diǎn)語(yǔ)法有什么優(yōu)勢(shì)呢?
省時(shí),省力 :如果使用了屬性,編譯器會(huì)自動(dòng)編寫(xiě)訪問(wèn)屬性所需的方法。這個(gè)過(guò)程由編譯器在編譯期執(zhí)行,看不到這些
get set
源代碼。編譯器會(huì)自動(dòng)向類(lèi)中添加適當(dāng)類(lèi)型的實(shí)例變量,并且在屬性名前添加下劃線(xiàn)。
如果你不想讓編譯器自動(dòng)合成存取方法,則可以自己實(shí)現(xiàn)。如果你只實(shí)現(xiàn)了其中一個(gè)存取方法,那么另一個(gè)還是會(huì)由編譯器來(lái)合成。
當(dāng)我們同時(shí)重寫(xiě)了setter and getter
方式時(shí),系統(tǒng)會(huì)報(bào)錯(cuò),原因是找不到實(shí)例變量。其解決方法: 在.m
的文件中使用@synthesize
。
@synthesize
是為屬性添加一個(gè)實(shí)例變量名,或者說(shuō)別名。同時(shí)會(huì)為該屬性生成 setter/getter
方法。
在protocol
中使用property
只會(huì)生成setter
和getter
方法聲明,我們使用屬性的目的,是希望遵守我協(xié)議的對(duì)象能實(shí)現(xiàn)該屬性。需要使用@synthesize
生成setter
和getter
。
當(dāng)你在子類(lèi)中重載了父類(lèi)中的屬性,你必須 使用@synthesize
來(lái)手動(dòng)合成ivar
。
當(dāng)我們同時(shí)重寫(xiě)了setter and getter
方式時(shí),需要在.m的文件中使用@synthesize
。
// Man.m#import "Man.h"@implementation Man@synthesize name = _name;// setter- (void)setName:(NSString *)name{ _name = name;}// getter- (NSString *)name{ return _name;}@end
@synthesize name = _name
_name
是成員變量
name
是屬性
作用是告訴編譯器name
屬性為_name
實(shí)例變量生成setter and getter
方法的實(shí)現(xiàn)
name
屬性的setter
方法是setName
,它操作的是_name
這個(gè)變量
在@synthesize
中定義與變量名不同的setter
和getter
的命名,以此來(lái)保護(hù)變量不會(huì)被不恰當(dāng)?shù)脑L問(wèn)(setter=<name>
這種不常用,也不推薦使用)
//setter=<name>這種不常用,也不推薦使用@property (nonatomic, setter = mySetter,getter = myGetter ) NSString *name;@property (nonatomic,getter = isHidden ) BOOL hidden;
@property
有兩個(gè)對(duì)應(yīng)的詞,一個(gè)是 @synthesize
,一個(gè)是 @dynamic
。如果 @synthesize
和 @dynamic
都沒(méi)寫(xiě),那么默認(rèn)的就是@syntheszie var = _var
。
如果某屬性已經(jīng)在某處實(shí)現(xiàn)了自己的 setter/getter
,可以使用 @dynamic
來(lái)阻止 @synthesize
自動(dòng)生成新的 setter/getter
覆蓋。
@dynamic
告訴編譯器:屬性的 setter
與 getter
方法由用戶(hù)自己實(shí)現(xiàn),不自動(dòng)生成。(當(dāng)然對(duì)于 readonly
的屬性只需提供 getter
即可)。
假如一個(gè)屬性被聲明為 @dynamic var
,然后你沒(méi)有提供 @setter
方法和 @getter
方法。編譯的時(shí)候沒(méi)問(wèn)題,但是當(dāng)程序運(yùn)行到 instance.var = someVar
,由于缺 setter
方法會(huì)導(dǎo)致程序崩潰?;蛘弋?dāng)運(yùn)行到 someVar = var
時(shí),由于缺 getter
方法同樣會(huì)導(dǎo)致崩潰。
編譯時(shí)沒(méi)問(wèn)題,運(yùn)行時(shí)才執(zhí)行相應(yīng)的方法,這就是所謂的動(dòng)態(tài)綁定。
// Man.h#import <Foundation/Foundation.h>@interface Man : NSObject@property (nonatomic,strong)NSString *name;@end// Man.m#import "Man.h"@implementation Man@dynamic name;// setter// - (void)setName:(NSString *)name// {// _name = name;// }// getter- (NSString *)name{ return _name;}@end
調(diào)用時(shí)會(huì)出現(xiàn)崩潰
Man *man = [[Man alloc] init]; man.name = @"sky";//缺 setter 方法會(huì)導(dǎo)致程序崩潰 NSString *name = man.name;//缺 getter 方法同樣會(huì)導(dǎo)致崩潰
atomic
(默認(rèn)):atomic
意為操作是原子的,意味著只有一個(gè)線(xiàn)程訪問(wèn)實(shí)例變量(生成的setter
和getter
方法是一個(gè)原子操作)。atomic
是線(xiàn)程安全的,至少在當(dāng)前的存取器上是安全的。它是一個(gè)默認(rèn)的特性,但是很少使用,因?yàn)楸容^影響效率。
nonatomic
:nonatomic
意為操作是非原子的,可以被多個(gè)線(xiàn)程訪問(wèn)。它的效率比atomic
快。但不能保證在多線(xiàn)程環(huán)境下的安全性,開(kāi)發(fā)中常用。
開(kāi)發(fā)iOS程序時(shí)應(yīng)該使用nonatomic
屬性,因?yàn)?code>atomic(同步鎖)屬性嚴(yán)重影響性能。該屬性使用了同步鎖,會(huì)在創(chuàng)建時(shí)生成一些額外的代碼用于幫助編寫(xiě)多線(xiàn)程程序,這會(huì)帶來(lái)性能問(wèn)題,通過(guò)聲明nonatomic
可以節(jié)省這些雖然很小但是不必要額外開(kāi)銷(xiāo)。
readwrite
(默認(rèn)):readwrite
是默認(rèn)值,表示該屬性同時(shí)擁有setter
和getter
。
readonly
: readonly
表示只有getter
沒(méi)有setter
。
有時(shí)候?yàn)榱苏Z(yǔ)意更明確可能需要自定義訪問(wèn)器的名字。
//setter=<name>這種不常用,也不推薦使用@property (nonatomic, setter = mySetter,getter = myGetter ) NSString *name; @property (nonatomic,getter = isHidden ) BOOL hidden;
assign
(默認(rèn)):assign
用于非指針變量(值)類(lèi)型,統(tǒng)一由系統(tǒng)棧進(jìn)行內(nèi)存管理。一般用于基礎(chǔ)類(lèi)型和C
數(shù)據(jù)類(lèi)型,如int
、float
、double
和NSInteger
,CGFloat
等表示單純的復(fù)制。還包括不存在所有權(quán)關(guān)系的對(duì)象,比如常見(jiàn)的delegate
。
retain
:在setter
方法中,需要對(duì)傳入的對(duì)象進(jìn)行引用計(jì)數(shù)加1
的操作。
strong
:strong
是在IOS
引入ARC
的時(shí)候引入的關(guān)鍵字,是retain
的一個(gè)可選的替代。對(duì)傳入的對(duì)象的強(qiáng)引用,會(huì)增加對(duì)象的引用計(jì)數(shù)。strong
跟retain
的意思相同并產(chǎn)生相同的代碼,但是語(yǔ)意上更好更能體現(xiàn)對(duì)象的關(guān)系。
weak
:對(duì)傳入的對(duì)象的弱引用,不增加對(duì)象的引用計(jì)數(shù),也不持有對(duì)象,當(dāng)對(duì)象消失后指針自動(dòng)指向nil
。
copy
:與strong
類(lèi)似,但區(qū)別在于copy
是創(chuàng)建一個(gè)新對(duì)象,strong
是創(chuàng)建一個(gè)指針,引用對(duì)象計(jì)數(shù)加1
。
weak
與strong
與copy
屬性特質(zhì)的差異Person
類(lèi)的實(shí)例變量,并分別用weak
與strong
修飾。@property (nonatomic,strong) Person *strongPerson;@property (nonatomic,weak) Person *weakPerson;
strongPerson
屬性置nil
。self.strongPerson = [[Person alloc] init];self.weakPerson = self.strongPerson;self.strongPerson = nil; NSLog(@"strongStr=%@,weakStr=%@",self.strongPerson,self.weakPerson);
輸出結(jié)果為:strongStr=(null),weakStr=(null)
。說(shuō)明weak修飾的屬性并不會(huì)使引用計(jì)數(shù)增加。
@property (nonatomic,strong) NSString *strongStr;@property (nonatomic,weak) NSString *weakStr;···self.strongStr = @"string";self.weakStr = self.strongStr;self.strongStr = nil;NSLog(@"strongStr=%@,weakStr=%@",self.strongStr,self.weakStr);
輸出結(jié)果為:strongStr=(null),weakStr=string
。這里主要是因?yàn)镹SString類(lèi)型的賦值默認(rèn)會(huì)加上copy,而copy會(huì)創(chuàng)建一個(gè)新的對(duì)象。這里的賦值語(yǔ)句其實(shí)是
self.strongStr = [@"string" copy];self.weakStr = [self.strongStr copy];
weakPerson
屬性置nil
。self.strongPerson = [[Person alloc] init];self.weakPerson = self.strongPerson;self.weakPerson = nil;NSLog(@"strongStr=%@,weakStr=%@",self.strongPerson,self.weakPerson);
輸出結(jié)果如下:strongStr=<Person: 0x600000007d50>,weakStr=(null)
。說(shuō)明weak修飾的屬性只是對(duì)對(duì)象的弱引用,并不會(huì)真正的持有該對(duì)象。
Person
類(lèi)實(shí)例變量p
,賦值strongPerson
后將p
置nil
。Person *p = [[Person alloc] init];self.strongPerson = p;self.weakPerson = self.strongPerson;p = nil; NSLog(@"strongStr=%@,weakStr=%@",self.strongPerson,self.weakPerson);
輸出結(jié)果為:strongStr=<Person: 0x600000200b50>,weakStr=<Person: 0x600000200b50>
。因?yàn)?code>strong屬性會(huì)強(qiáng)引用該對(duì)象并使該對(duì)象的引用計(jì)數(shù)+1
,所以即使把p
設(shè)置為nil
,該對(duì)象也并沒(méi)有釋放,要想釋放該對(duì)象,還得把strongStr
設(shè)置為nil:self.strongPerson = nil;
。這樣輸出結(jié)果才為 strongStr=(null),weakStr=(null)
。
Person
類(lèi)加了一個(gè)name
屬性。并用copy
修飾 :(@property (nonatomic,copy) NSString *name
)。NSString *a = @"xiaoming";Person *p = [[Person alloc] init];p.name = a;NSLog(@"before p.name=%@",p.name);a = @"xiaohua";NSLog(@"after p.name=%@",p.name);
輸出結(jié)果:before p.name=xiaoming
與 after p.name=xiaoming
。因?yàn)?code>copy關(guān)鍵字修飾的屬性是將對(duì)象拷貝一份賦值,所以你改變?cè)瓕?duì)象并不會(huì)對(duì)拷貝后的對(duì)象有任何改變。
注:用@property
聲明 NSString
、NSArray
、NSDictionary
經(jīng)常使用copy
關(guān)鍵字,是因?yàn)樗麄冇袑?duì)應(yīng)的可變類(lèi)型:NSMutableString
、NSMutableArray
、NSMutableDictionary
,他們之間可能進(jìn)行賦值操作,為確保對(duì)象中的字符串值不會(huì)無(wú)意間變動(dòng),應(yīng)該在設(shè)置新屬性值時(shí)拷貝一份.
可以用@property
語(yǔ)法來(lái)定義對(duì)象中所封裝的數(shù)據(jù)。
通過(guò)“修飾詞”來(lái)指定存儲(chǔ)數(shù)據(jù)所需的正確語(yǔ)義。
在設(shè)置屬性所對(duì)應(yīng)的實(shí)例變量時(shí),一定要遵從該屬性所聲明的語(yǔ)義。
開(kāi)發(fā)iOS程序時(shí)應(yīng)該使用nonatomic
屬性,因?yàn)?code>atomic(同步鎖)屬性嚴(yán)重影響性能。
聯(lián)系客服