Objective-C中的@property
1.@property是什么
@Property是聲明屬性的語法,它可以快速方便的為實例變量創(chuàng)建存取器,并允許我們通過點語法使用存取器。
存取器(accessor):指用于獲取和設(shè)置實例變量的方法。用于獲取實例變量值的存取器是getter,用于設(shè)置實例變量值的存取器是setter。
2.創(chuàng)建存取器
2.1 手工創(chuàng)建存取器
我們先看兩段代碼:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | //?Car.h #import?<foundation foundation.h=""> @interface?Car?:?NSObject { ????//?實例變量 ????NSString?*carName; ????NSString?*carType; } //?setter??????????????????????????????????? -?(void)setCarName:(NSString?*)newCarName;? //?getter -?(NSString?*)carName; //?setter -?(void)setCarType:(NSString?*)newCarType; //?getter -?(NSString?*)carType; @end</foundation> |
上面的代碼中carName和carType就是Car的實例變量,并且可以看到分別對這兩個實例變量聲明了get/set方法,即存取器。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #import?"Car.h" @implementation?Car //?setter -?(void)setCarName:(NSString?*)newCarName { ????carName?=?newCarName; } //?getter -?(NSString?*)carName { ????return?carName; } //?setter -?(void)setCarType:(NSString?*)newCarType { ????carType?=?newCarType; } //?getter -?(NSString?*)carType { ????return?carType; } @end |
上面代碼是對實例變量存取器的實現(xiàn)。我們可以看到,存取器就是對實例變量進行賦值和取值。按照約定賦值方法以set開頭,取值方法以實例變量名命名。
我們看看如何使用:
| 1 2 3 4 5 6 7 8 9 10 11 12 | //?main.m #import?"Car.h" int?main(int?argc,?char?*?argv[]) { ????@autoreleasepool?{ ????????Car?*car?=?[[Car?alloc]?init]; ????????[car?setCarName:@"Jeep?Cherokee"]; ????????[car?setCarType:@"SUV"]; ????????NSLog(@"The?car?name?is?%@?and?the?type?is?%@",[car?carName],[car?carType]);?????? ????} ????return?0; } |
上面的代碼中我們注意到,對象Car使用了消息語法,也就是使用方括號的語法給存取器發(fā)送消息。 返回結(jié)果為:
| 1 | The?car?name?is?Jeep?Cherokee?and?the?type?is?SUV |
2.2 使用@Property創(chuàng)建存取器
| 1 2 3 4 5 6 7 8 9 10 11 | //?Car.h #import?<foundation foundation.h=""> @interface?Car?:?NSObject { ????//?實例變量 ????NSString?*carName; ????NSString?*carType; } @property(nonatomic,strong)?NSString?*carName; @property(nonatomic,strong)?NSString?*carType; @end</foundation> |
上面代碼中,我們使用@property聲明兩個屬性,名稱與實例變量名稱相同(讓我們先忽略nonatomic和strong)。
| 1 2 3 4 5 6 | //?Car.m #import?"Car.h" @implementation?Car @synthesize?carName; @synthesize?carType; @end |
在.m文件中我們使用@synthesize自動生成這兩個實例變量的存取器,并且隱藏了存取器,雖然我們看不到存取器,但它們確實是存在的。
| 1 2 3 4 5 6 7 8 9 10 11 | //?main.m int?main(int?argc,?char?*?argv[]) { ????@autoreleasepool?{ ????????Car?*car?=?[[Car?alloc]?init]; ????????car.carName?=?@"Jeep?Compass"; ????????car.carType?=?@"SUV"; ????????NSLog(@"The?car?name?is?%@?and?the?type?is?%@",car.carName,car.carType);? ????} ????return?0; } |
在上面的代碼中我們可以注意到,Car對象使用點語法給存取器發(fā)送消息,并且get與set的語法是相同的,所以這里的點語法可以根據(jù)語境判斷我們是要賦值還是取值。
當然我們也依然可以使用消息語法來使用:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | //?main.m int?main(int?argc,?char?*?argv[]) { ????@autoreleasepool?{ ????????Car?*car?=?[[Car?alloc]?init]; ????????//?點語法 //????????car.carName?=?@"Jeep?Compass"; //????????car.carType?=?@"SUV"; //????????NSLog(@"The?car?name?is?%@?and?the?type?is?%@",car.carName,car.carType); ????????//?消息語法 ????????[car?setCarName:@"Jeep?Compass"]; ????????[car?setCarType:@"SUV"]; ????????NSLog(@"The?car?name?is?%@?and?the?type?is?%@",[car?carName],[car?carType]);??????? ????} ????return?0; } |
上面兩段代碼的執(zhí)行結(jié)果都是:
| 1 | The?car?name?is?Jeep?Compass?and?the?type?is?SUV |
總結(jié):@property等同于在.h文件中聲明實例變量的get/set方法,@synthesize等同于在.m文件中實現(xiàn)實例變量的get/set方法。使用@property和synthesize創(chuàng)建存取器要比手動聲明兩個存取方法(getter和setter)更簡單。而且我們在使用屬性時可以使用點語法賦值或取值,語法更簡單,更符合面向?qū)ο缶幊獭?/strong>
3.不必單獨聲明示例變量
如果使用@Property,就不必單獨聲明實例變量了。因為在沒有顯示提供示例變量聲明的前提下,系統(tǒng)會自動幫你生成實例變量。我們通過以下代碼來說明:
| 1 2 3 4 5 6 7 | //?Car.h #import?<foundation foundation.h=""> @interface?Car?:?NSObject @property(nonatomic,strong)?NSString?*carName; @property(nonatomic,strong)?NSString?*carType; -?(NSString?*)carInfo; @end</foundation> |
在.h文件中我們并沒有聲明實例變量,只是聲明了carName和carType兩個屬性,以及一個carInfo方法,返回值為NSString *。
| 1 2 3 4 5 6 7 8 | //?Car.m #import?"Car.h" @implementation?Car -?(NSString?*)carInfo { ????return?[NSString?stringWithFormat:@"The?car?name?is?%@?and?the?type?is?%@",_carName,_carType]; } @end |
在.m文件中我們可以注意到,在carInfo方法中我們使用了_carName和_carType實例變量,這就是當我們沒有顯示聲明實例變量時,系統(tǒng)為我們自動生成的。命名規(guī)則是以_為前綴,加上屬性名,即_propertyName。
其實在.m文件中實際是存在@synthesize聲明語句的,只是系統(tǒng)將其隱藏了:
| 1 2 | @synthesize?carName?=?_carName; @synthesize?carType?=?_carType; |
那么如果我們不喜歡默認的實例變量命名方法,或者我們希望使用更有語義的名稱,應(yīng)該怎么做呢。其實很簡單:
| 1 2 3 4 5 6 7 8 9 10 | //?Car.m #import?"Car.h" @implementation?Car @synthesize?carName?=?i_am_car_name; @synthesize?carType?=?i_am_car_type; -?(NSString?*)carInfo { ????return?[NSString?stringWithFormat:@"The?car?name?is?%@?and?the?type?is?%@",i_am_car_name,i_am_car_type]; } @end |
通過上述代碼可以看到,我們只需要通過@synthesize來聲明我們希望的實例變量名。
總結(jié):如果我們希望使用默認的實例變量命名方式,那么我們在.m文件中就不需要使用@synthesize聲明,系統(tǒng)會幫我們自動完成。如果我們希望自己命名實例變量命,那么我們就使用@synthesize顯示聲明我們希望的實例變量名。
4.@property的特性
@property還有一些關(guān)鍵字,它們都是有特殊作用的,比如上述代碼中的nonatomic,strong:
| 1 2 | @property(nonatomic,strong)?NSString?*carName; @property(nonatomic,strong)?NSString?*carType; |
我把它們分為三類,分別是:原子性,存取器控制,內(nèi)存管理。
4.1 原子性
atomic(默認):atomic意為操作是原子的,意味著只有一個線程訪問實例變量。atomic是線程安全的,至少在當前的存取器上是安全的。它是一個默認的特性,但是很少使用,因為比較影響效率,這跟ARM平臺和內(nèi)部鎖機制有關(guān)。
nonatomic:nonatomic跟atomic剛好相反。表示非原子的,可以被多個線程訪問。它的效率比atomic快。但不能保證在多線程環(huán)境下的安全性,在單線程和明確只有一個線程訪問的情況下廣泛使用。
4.2 存取器控制
readwrite(默認):readwrite是默認值,表示該屬性同時擁有setter和getter。
readonly: readonly表示只有g(shù)etter沒有setter。
有時候為了語意更明確可能需要自定義訪問器的名字:
| 1 | @property?(nonatomic,?setter?=?mySetter:,getter?=?myGetter?)?NSString?*name; |
最常見的是BOOL類型,比如標識View是否隱藏的屬性hidden。可以這樣聲明:
| 1 | @property?(nonatomic,getter?=?isHidden?)?BOOL?hidden; |
4.3 內(nèi)存管理
@property有顯示的內(nèi)存管理策略。這使得我們只需要看一眼@property聲明就明白它會怎樣對待傳入的值。
assign(默認):assign用于值類型,如int、float、double和NSInteger,CGFloat等表示單純的復(fù)制。還包括不存在所有權(quán)關(guān)系的對象,比如常見的delegate。
| 1 | @property(nonatomic)?int?running; |
| 1 | @property(nonatomic,assign)?int?running; |
以上兩段代碼是相同的。
在setter方法中,采用直接賦值來實現(xiàn)設(shè)值操作:
| 1 2 3 | -(void)setRunning:(int)newRunning{?? ????_running?=?newRunning;?? } |
retian:在setter方法中,需要對傳入的對象進行引用計數(shù)加1的操作。
簡單來說,就是對傳入的對象擁有所有權(quán),只要對該對象擁有所有權(quán),該對象就不會被釋放。如下代碼所示:
| 1 2 3 4 5 6 7 8 | -(void)setName:(NSString*)_name{?? ?????//首先判斷是否與舊對象一致,如果不一致進行賦值。?? ?????//因為如果是一個對象的話,進行if內(nèi)的代碼會造成一個極端的情況:當此name的retain為1時,使此次的set操作讓實例name提前釋放,而達不到賦值目的。?? ?????if?(?name?!=?_name){?? ??????????[name?release];?? ??????????name?=?[_name?retain];?? ?????}?? } |
strong:strong是在IOS引入ARC的時候引入的關(guān)鍵字,是retain的一個可選的替代。表示實例變量對傳入的對象要有所有權(quán)關(guān)系,即強引用。strong跟retain的意思相同并產(chǎn)生相同的代碼,但是語意上更好更能體現(xiàn)對象的關(guān)系。
weak:在setter方法中,需要對傳入的對象不進行引用計數(shù)加1的操作。
簡單來說,就是對傳入的對象沒有所有權(quán),當該對象引用計數(shù)為0時,即該對象被釋放后,用weak聲明的實例變量指向nil,即實例變量的值為0。
注:weak關(guān)鍵字是IOS5引入的,IOS5之前是不能使用該關(guān)鍵字的。delegate 和 Outlet 一般用weak來聲明。
copy:與strong類似,但區(qū)別在于實例變量是對傳入對象的副本擁有所有權(quán),而非對象本身。
轉(zhuǎn)載于:https://www.cnblogs.com/holyday/p/7987059.html
總結(jié)
以上是生活随笔為你收集整理的Objective-C中的@property的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: pajek中文乱码:ASCII字符处理
- 下一篇: 解决vuex中store保存数据,刷新页