11 属性

说明:O-C 2.0引入了属性(property),即@property预编译指令,它组合了新的预编译指令和新的属性访问器语法。
用途:除非自己定义了相关代码,否则@property会根据特性自动生成属性及其settergetter的声明和实现。
兼容性:10.5+
语法:@property[(特性)] 实例变量类型 实例变量名;
注意:还有一个编译指令@synthesize,用来配合@property生成gettersetter的实现,可以省略(XCode 4.4之后)。

11.1 使用属性值

AllWeatherRadial.h

说明:对需要设置settergetter的实例变量使用@property

1
2
3
4
5
6
7
8
9
10
11
12
#import "Tire.h"

@interface AllWeatherRadial : Tire
{
// 轮胎再潮湿的道路上的性能
float rainHandling;
float snowHandling;
}
// 对需要设置setter和getter的实例变量使用@property(不在需要为每个属性分别声明setter和getter)
@property float rainHandling;
@property float snowHandling;
@end// AllWeatherRadial

AllWeatherRadial.m

说明:不需要实现被设置了@propery的实例属性的gettersetter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#import "AllWeatherRadial.h"

@implementation AllWeatherRadial
- (id) initWithPressure: (float) p treadDepth: (float) td {
if (self = [super initWithPressure: p treadDepth:td]) {
// ...
}
return (self);
}

/**
* @override
*/

- (NSString *) description {
NSString *desc;
desc = [[NSString alloc] initWithFormat:@"AllWeatherRadial: %.1f / %.1f / %.1f / %.1f", [self pressure], [self treadDepth], [self rainHandling], [self snowHandling]];
return (desc);
}
@end

main.m

说明:调用相应的属性的settergetter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#import "AllWeatherRadial.h"
#import "Car.h"
#import "Slant6.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 车身
Car *car = [[Car alloc] init];
// 安装轮胎
for (int i = 0; i < 4; i++) {
AllWeatherRadial *tire;
tire = [[AllWeatherRadial alloc] init];
// 调用通过@property指令获得的setter和getter
[tire setRainHandling:23 + i];
[tire setSnowHandling:33 - i];
NSLog(@"tire %d's handling is %.f %.f", i, [tire rainHandling], [tire snowHandling]);
[car setTire:tire atIndex:i];
[tire release];
}
// 安装引擎
Engine *engine = [[Slant6 alloc] init];
[car setEngine:engine];
// 使用Car
[car print];
[car release];
}
return 0;
}

11.1.1 简化接口代码

11.1.2 简化实现代码

11.1.3 点表达式的妙用

说明:O-C 2.0引入的新的语法特性,可以更加容易地访问对象的属性。
限制:只能用于对象属性的settergetter

1
2
3
4
5
6
// getter
tire.rainHandling = 20 + i;
tire.snowHandling = 28 + i;

// setter
NSLog(@"tire %d's handling is %.f %.f", i, tire.rainHandling, tire.snowHandling);

11.2 属性扩展

说明:@property对如何生成代码还有一些特性可以指定,这些特性将影响setter代码的生成。

@property特性 说明 适用 备注
assign 简单赋值,不更改引用计数 基础数据类型和C数据类型 默认
copy 通过就对象复制出一个新对象(引用计数为1),并释放旧对象 不可变对象(例如NSString)
retain 释放旧对象,将旧对象的值赋予输入对象,再保留输入对象 其它O-C对象
nonatomic 非线程安全 所有类型 默认,性能更好
atomic 某种程度的线程安全 所有类型 在多线程的环境保证getset正确执行,但前提是是使用@synthesize生成的实现
readwrite 可读写 所有类型 默认
readonly 只读 所有类型 只生成getter
getter=getter名称 指定生成的getter方法名 所有类型 默认为属性名
setter=setter名称 指定生成的setter方法名 所有类型 set属性名,匈牙利命名法

使用@property

说明:name属性为@property(copy)engine属性为@property(retain)

Car.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#import <Cocoa/Cocoa.h>

@class Tire;
@class Engine;

@interface Car : NSObject
{
NSString *name;
Engine *engine;
...
}
- (NSString *) name;
- (void) setName: (NSString *) newName;
- (Engine *) engine;
- (void) setEngine: (Engine *) newEngine;

@property (retain) Engine *engine;
...
@end // Car

Car.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#import "Car.h"
#import "Engine.h"
#import "Tire.h"

@implementation Car
- (NSString *) name {
return (name);
}// name
- (void) setName: (NSString *) newName {
[name release];
name = [newName copy];
}// setName

- (Engine *) engine {
return (engine);
} // engine
- (void) setEngine: (Engine *) newEngine {
[newEngine retain];
[engine release];
engine = newEngine;
} // setEngine

...
@end // Car

不使用@property

Car.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#import <Cocoa/Cocoa.h>

@class Tire;
@class Engine;

@interface Car : NSObject
{
NSString *name;
Engine *engine;
...
}
// 使用copy特性
@property(copy) NSString name;
// 使用retain特性
@property(retain) Engine *engine;
...
@end // Car

Car.m

说明:@synthesize用来生成成员变量的settergetter的实现。

1
2
3
4
5
6
7
8
9
10
#import "Car.h"
#import "Engine.h"
#import "Tire.h"

@implementation Car
// xcode4.4 之后@synthesize就不再需要了
@synthesize name;
@synthesize engine;
...
@end// Car

11.2.1 名称的使用

Car.h

1
2
3
4
5
6
7
8
9
10
11
12
#import <Cocoa/Cocoa.h>

@class Tire;
@class Engine;

@interface Car : NSObject
{
NSString *appellation;
...
}
@property (copy) NSString *name;
...

Car.m

1
2
3
4
5
6
7
8
#import "Car.h"
#import "Engine.h"
#import "Tire.h"

@implementation Car
@synthesize name = appellation;
...
@end

11.2.2 只读属性

说明:假设某个属性,不想让任何人修改它,则可以对这个@property使用readonly特性,这时,只生成一个getter方法而不会生成setter

1
2
3
4
5
6
7
8
@interface Me: NSObject
{
float showSize;
NSString *licenseNumber;
}
@property (readonly) float showSize;
@property (readonly) NSString *licenseNumber;
@end

11.2.3 自己动手有时更好

计算属性

说明:可以通过@property配合@dynamic指令告诉编译器不生成人和代码或实例变量,通过自定义的getter创建一个能在运行时计算出此值的访问方法。
注意:如果使用了@dynamic指令,并企图调用不存在的gettersetter方法,你将会的到一个报错。

1
2
3
4
5
6
7
8
// @property
@property (readonly) float bodyMassIndex;
// @dynamic
@dynamic bodyMassIndex;
// getter
- (float) bodyMassIndex {
// ...
}

指定getter和setter方法名

说明:可以通过@property(getter=getter名称)@property(setter=setter名称)自定义getersetter方法的名称。

1
2
// getter为isHidden,setter为setHidden(默认)
@property (getter=isHidden) BOOL hidden;

11.2.4 特性不是万能的

说明:@property只能生成严格意义上的gettersetter,不支持那些需要接收额外参数的方法。

1
2
3
4
// setter有额外的参数
- (void) setTire: (Tire *) tire atIndex: (int) index;
// getter有额外的参数
- (Tire *) tireAtIndex: (int) index;

11.3 小结