[email protected]是什麼
@Property
是聲明屬性的文法,它可以快速友善的為執行個體變量建立存取器,并允許我們通過點文法使用存取器。
存取器(accessor):指用于擷取和設定執行個體變量的方法。用于擷取執行個體變量值的存取器是,用于設定執行個體變量值的存取器是
getter
。
setter
2.建立存取器
2.1 手工建立存取器
我們先看兩段代碼:
// 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
上面的代碼中
carName
和
carType
就是
Car
的執行個體變量,并且可以看到分别對這兩個執行個體變量聲明了get/set方法,即存取器。
#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
上面代碼是對執行個體變量存取器的實作。我們可以看到,存取器就是對執行個體變量進行指派和取值。按照約定指派方法以set開頭,取值方法以執行個體變量名命名。
我們看看如何使用:
// 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
使用了消息文法,也就是使用方括号的文法給存取器發送消息。 傳回結果為:
The car name is Jeep Cherokee and the type is SUV
2.2 使用@Property建立存取器
// Car.h
#import <Foundation/Foundation.h>
@interface Car : NSObject
{
// 執行個體變量
NSString *carName;
NSString *carType;
}
@property(nonatomic,strong) NSString *carName;
@property(nonatomic,strong) NSString *carType;
@end
上面代碼中,我們使用
@property
聲明兩個屬性,名稱與執行個體變量名稱相同(讓我們先忽略
nonatomic
和
strong
)。
// Car.m
#import "Car.h"
@implementation Car
@synthesize carName;
@synthesize carType;
@end
在.m檔案中我們使用
@synthesize
自動生成這兩個執行個體變量的存取器,并且隐藏了存取器,雖然我們看不到存取器,但它們确實是存在的。
// 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
對象使用點文法給存取器發送消息,并且get與set的文法是相同的,是以這裡的點文法可以根據語境判斷我們是要指派還是取值。
當然我們也依然可以使用消息文法來使用:
// 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;
}
上面兩段代碼的執行結果都是:
The car name is Jeep Compass and the type is SUV
總結:等同于在.h檔案中聲明執行個體變量的get/set方法,
@property
等同于在.m檔案中實作執行個體變量的get/set方法。使用
@synthesize
和
@property
建立存取器要比手動聲明兩個存取方法(
synthesize
和
getter
)更簡單。而且我們在使用屬性時可以使用點文法指派或取值,文法更簡單,更符合面向對象程式設計。
setter
3.不必單獨聲明示例變量
如果使用
@Property
,就不必單獨聲明執行個體變量了。因為在沒有顯示提供示例變量聲明的前提下,系統會自動幫你生成執行個體變量。我們通過以下代碼來說明:
// Car.h
#import <Foundation/Foundation.h>
@interface Car : NSObject
@property(nonatomic,strong) NSString *carName;
@property(nonatomic,strong) NSString *carType;
- (NSString *)carInfo;
@end
在.h檔案中我們并沒有聲明執行個體變量,隻是聲明了
carName
和
carType
兩個屬性,以及一個
carInfo
方法,傳回值為
NSString *
。
// 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
執行個體變量,這就是當我們沒有顯示聲明執行個體變量時,系統為我們自動生成的。命名規則是以
_
為字首,加上屬性名,即
_propertyName
。
其實在.m檔案中實際是存在
@synthesize
聲明語句的,隻是系統将其隐藏了:
@synthesize carName = _carName;
@synthesize carType = _carType;
那麼如果我們不喜歡預設的執行個體變量命名方法,或者我們希望使用更有語義的名稱,應該怎麼做呢。其實很簡單:
// 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
來聲明我們希望的執行個體變量名。
總結:如果我們希望使用預設的執行個體變量命名方式,那麼我們在.m檔案中就不需要使用聲明,系統會幫我們自動完成。如果我們希望自己命名執行個體變量命,那麼我們就使用
@synthesize
顯示聲明我們希望的執行個體變量名。
@synthesize
[email protected]的特性
@property
還有一些關鍵字,它們都是有特殊作用的,比如上述代碼中的
nonatomic
,
strong
:
@property(nonatomic,strong) NSString *carName;
@property(nonatomic,strong) NSString *carType;
我把它們分為三類,分别是:原子性,存取器控制,記憶體管理。
4.1 原子性
-
(預設):atomic
意為操作是原子的,意味着隻有一個線程通路執行個體變量。atomic是線程安全的,至少在目前的存取器上是安全的。它是一個預設的特性,但是很少使用,因為比較影響效率,這跟ARM平台和内部鎖機制有關。atomic
-
:nonatomic
跟nonatomic
剛好相反。表示非原子的,可以被多個線程通路。它的效率比atomic快。但不能保證在多線程環境下的安全性,在單線程和明确隻有一個線程通路的情況下廣泛使用。atomic
4.2 存取器控制
-
(預設):readwrite
是預設值,表示該屬性同時擁有readwrite
和setter
。getter
-
:readonly
表示隻有readonly
沒有getter
。setter
有時候為了語意更明确可能需要自定義通路器的名字:
@property (nonatomic, setter = mySetter:,getter = myGetter ) NSString *name;
最常見的是BOOL類型,比如辨別View是否隐藏的屬性hidden。可以這樣聲明:
@property (nonatomic,getter = isHidden ) BOOL hidden;
4.3 記憶體管理
@property
有顯示的記憶體管理政策。這使得我們隻需要看一眼
@property
聲明就明白它會怎樣對待傳入的值。
-
(預設):assign
用于值類型,如assign
、int
、float
和double
,NSInteger
等表示單純的複制。還包括不存在所有權關系的對象,比如常見的delegate。CGFloat
@property(nonatomic) int running;
@property(nonatomic,assign) int running;
以上兩段代碼是相同的。
在
setter
方法中,采用直接指派來實作設值操作:
-(void)setRunning:(int)newRunning{
_running = newRunning;
}
-
:在retian
setter
方法中,需要對傳入的對象進行引用計數加1的操作。
簡單來說,就是對傳入的對象擁有所有權,隻要對該對象擁有所有權,該對象就不會被釋放。如下代碼所示:
-(void)setName:(NSString*)_name{
//首先判斷是否與舊對象一緻,如果不一緻進行指派。
//因為如果是一個對象的話,進行if内的代碼會造成一個極端的情況:當此name的retain為1時,使此次的set操作讓執行個體name提前釋放,而達不到指派目的。
if ( name != _name){
[name release];
name = [_name retain];
}
}
-
:strong
是在IOS引入ARC的時候引入的關鍵字,是retain的一個可選的替代。表示執行個體變量對傳入的對象要有所有權關系,即強引用。strong跟retain的意思相同并産生相同的代碼,但是語意上更好更能展現對象的關系。strong
-
:在weak
setter
方法中,需要對傳入的對象不進行引用計數加1的操作。
簡單來說,就是對傳入的對象沒有所有權,當該對象引用計數為0時,即該對象被釋放後,用
聲明的執行個體變量指向weak
,即執行個體變量的值為0。nil
注:
關鍵字是IOS5引入的,IOS5之前是不能使用該關鍵字的。delegate 和 Outlet 一般用weak
來聲明。weak
-
:與copy
類似,但差別在于執行個體變量是對傳入對象的副本擁有所有權,而非對象本身。strong
- 原文位址:http://www.devtalking.com/articles/you-should-to-know-property/