您的位置:首页 > 移动开发 > Objective-C

Objective-C中的@property

2015-07-23 10:02 543 查看
1.@property是什么

@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</foundation>


上面的代码中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.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</foundation>


上面的代码中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</foundation>


上面代码中,我们使用@property声明两个属性,名称与实例变量名称相同

// 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


总结:@property等同于在.h文件中声明实例变量的get/set方法,@synthesize等同于在.m文件中实现实例变量的get/set方法。使用@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</foundation>


在.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显示声明我们希望的实例变量名。

4.@property的特性

@property还有一些关键字,它们都是有特殊作用的,比如上述代码中的nonatomic,strong:

@property(nonatomic,strong) NSString *carName;
@property(nonatomic,strong) NSString *carType;


我把它们分为三类,分别是:原子性,存取器控制,内存管理。

4.1 原子性

atomic(默认):atomic意为操作是原子的,意味着只有一个线程访问实例变量。atomic是线程安全的,至少在当前的存取器上是安全的。它是一个默认的特性,但是很少使用,因为比较影响效率,这跟ARM平台和内部锁机制有关。

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,CGFloat等表示单纯的复制。还包括不存在所有权关系的对象,比如常见的delegate。

@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:strong是在IOS引入ARC的时候引入的关键字,是retain的一个可选的替代。表示实例变量对传入的对象要有所有权关系,即强引用。strong跟retain的意思相同并产生相同的代码,但是语意上更好更能体现对象的关系。

weak:在setter方法中,需要对传入的对象不进行引用计数加1的操作。

简单来说,就是对传入的对象没有所有权,当该对象引用计数为0时,即该对象被释放后,用weak声明的实例变量指向nil,即实例变量的值为0。

注:weak关键字是IOS5引入的,IOS5之前是不能使用该关键字的。delegate 和 Outlet 一般用weak来声明。

copy:与strong类似,但区别在于实例变量是对传入对象的副本拥有所有权,而非对象本身。

atomic:原子属性,为setter方法加锁(默认就是atomic)

nonatomic:非原子属性,不会为setter方法加锁

atomic加锁原理

@property (assign, atomic) int age;

- (void)setAge:(int)age
{

@synchronized(self) {
_age = age;
}
}


从CocoaChina转载

原地址:/article/1225654.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: