1使用属性值
我们转换了一个比较简单的类(AllWeatherRadial)以使用属性。我们假设有人从不同的商店购买四个降价销售的轮胎,因此,这四个轮胎具有不同的性能值
//main()函数
Car *car = [[Car alloc] init];
for(int i = 0;i < 4;i++)
{
AllWeatherRadial *tire;
tire = [[AllWeatherRadial alloc] init];
[tire setRainHanding:20+i];
[tire setSnowHanding:28+i];
NSLog(@"tire %'s handling is %.f %.f",i,[tire rainHandling];[tire snowHandling]);
[car setTire:tire atIndex:i];
}
Engine *engine = [[Slant6 alloc] init];
[car setEngine:engine];
[car print];
1.1简化接口代码
现在我们来研究一下,AllWeatherRadial类的接口代码
@interface AllWeatherRadial:Tire
{
float rainHandling;
float snowHandling;
}
-(void)setRainHandling:(float)rainHandling;
-(float)rainHandling;
-(void)setSnowHandling:(float)snowHandling;
-(float)snowHandling;
@end
这种写法已经过时,我们将其改写为具有属性风格的新形式
@interface AllWeatherRadial:Tire
{
float rainHandling;
float snowHandling;
}
@property float rainHandling;
@property float snowHandling;
@end
- @property是一种新的编译器功能,它意味着声明了一个新对象的属性
- @property float rainHandling;这句话表明AllWeatherRadial类的对象具有float类型的属性,其名称为rainHandling。
- 而且你也可以通过调用-setRainHandling:来设置属性,通过-rainHandling来访问属性
- @property预编译指令的作用是自动声明属性的setter和getter方法
1.2简化实现代码
AllWeatherRadial的简化实现
@implementation AllWeatherRadial
@synthesize rainHandling;
@synthesize snowHandling;
-(id)initWithPressure:(float) p treadDepth:(float) td{
if(self = [super initWithPressure:p treadDepth:td]){
rainHandling = 23.7;
snowHandling = 42.5;
}
return self;
}
-(NSString *)description{
NSString *desc;
desc = [[NSString alloc] initWithFormat:@"AllweatherRadial:%.1f / %.1f / %.1f / %.1f",[self pressure],[self treadDepth],[self rainHandling],[self snowHandling]];
return desc;
}
- @synthesize也是一种新的编译器功能,它表示“创建了该属性的访问代码”(Xcode4.5之后可以不必使用@synthesize)
- 当遇到@synthesize rainHandling;这行代码时,编译器将添加实现-setRainHandling:和-rainHandling方法的预编译代码
- 所有的属性都是基于变量的,所以在你合成setter和getter方法的时候,编译器会自动创建与属性名称相同的实例变量
- 我们的头文件有两个叫做rainHandling和snowHandling的实例变量,如果你没有声明这些变量,编译器也会声明的
- 有两个地方可以用来添加实例变量声明,头文件和声明文件。假设你有一个子类,并且你想要从子类直接通过属性访问变量,这种情况下,语句必须放在头文件中。如果是当前类访问,则可以放在声明文件中
修改后的代码
//接口
@interface AllWeatherRadial:Tire
@property float rainHandling;
@property float snowHandling;
@end
//实现
@implementation AllWeatherRadial
@synthesize rainHandling;
@synthesize snowHandling;
-(id)initWithPressure:(float) p treadDepth:(float) td{
if(self = [super initWithPressure:p treadDepth:td]){
rainHandling = 23.7;
snowHandling = 42.5;
}
return self;
}
-(NSString *)description{
NSString *desc;
desc = [[NSString alloc] initWithFormat:@"AllweatherRadial:%.1f / %.1f / %.1f / %.1f",[self pressure],[self treadDepth],[self rainHandling],[self snowHandling]];
return desc;
}
1.3点表达式的妙用
回想一下我们在main函数中添加的用于修改轮胎性能值得两行代码
[tire setRainHandling:20+i];
[tire setSnowHandling:28+i];
我们可以将其替换为下面的代码
tire.rainHandling:20+i;
tire.snowHandling:28+i;
如果点表达式出现在了=的左边,该变量名称的setter方法将被调用
如果点表达式出现在了=的右边,该变量名称的getter方法将被调用
2.属性扩展
我们使用访问方法来访问对象时需要保留和释放对象。对于某些对象的值,尤其是字符串的值,你总是会复制(-copy)它们。而对于其他对象的值,如委托(后面会说)你根本不会想要保留它们。
下面给Car类添加一种新的特性,这样我们就可以使用一些新的语法了。
我们在Car对象中添加一个新的实例变量name
#import
@interface Tire : NSObject
@end
@implementation Tire
- (NSString *) description{
return (@"a tire");
}
@end//Tire
@interface Engine : NSObject
@end
@implementation Engine
- (NSString *) description{
return (@"a engine");
}
@end//Engine
@interface Car : NSObject{
NSString *name;
Engine *engine;
Tire *tires[4];
}
-(void)setName:(NSString *)newName;
-(NSString *)name;
-(void)print;
@end
@implementation Car
-(id)init{
if (self = [super init]) {
engine = [Engine new];
tires[0] = [Tire new];
tires[1] = [Tire new];
tires[2] = [Tire new];
tires[3] = [Tire new];
}
return self;
}
-(void)setName:(NSString *)newName{
name = [newName copy];
}
-(NSString *)name{
return name;
}
-(void)print{
NSLog(@"%@ has:",name);
NSLog(@"%@",engine);
NSLog(@"%@",tires[0]);
NSLog(@"%@",tires[1]);
NSLog(@"%@",tires[2]);
NSLog(@"%@",tires[3]);
}
@end//Car
int main(int argc, const char * argv[]) {
@autoreleasepool{
Car *car = [[Car alloc] init];
[car setName:@"Herbie"];
[car print];
}
return 0;
}
//运行结果
2018-10-28 09:24:11.474592-0700 Car-New[981:120418] Herbie has:
2018-10-28 09:24:11.474825-0700 Car-New[981:120418] a engine
2018-10-28 09:24:11.475023-0700 Car-New[981:120418] a tire
2018-10-28 09:24:11.475049-0700 Car-New[981:120418] a tire
2018-10-28 09:24:11.475058-0700 Car-New[981:120418] a tire
2018-10-28 09:24:11.475065-0700 Car-New[981:120418] a tire
Program ended with exit code: 0
我们开始想Car类添加属性
#import
@interface Tire : NSObject
@end
@implementation Tire
- (NSString *) description{
return (@"a tire");
}
@end//Tire
@interface Engine : NSObject
@end
@implementation Engine
- (NSString *) description{
return (@"a engine");
}
@end//Engine
@interface Car : NSObject{
Engine *engine;
Tire *tires[4];
}
@property (copy) NSString *name;
-(void)print;
@end
@implementation Car
-(id)init{
if (self = [super init]) {
engine = [Engine new];
tires[0] = [Tire new];
tires[1] = [Tire new];
tires[2] = [Tire new];
tires[3] = [Tire new];
}
return self;
}
@synthesize name;
-(void)print{
NSLog(@"%@ has:",name);
NSLog(@"%@",engine);
NSLog(@"%@",tires[0]);
NSLog(@"%@",tires[1]);
NSLog(@"%@",tires[2]);
NSLog(@"%@",tires[3]);
}
@end//Car
int main(int argc, const char * argv[]) {
@autoreleasepool{
Car *car = [[Car alloc] init];
car.name = @"Herbie";
[car print];
}
return 0;
}
//运行结果
2018-10-28 09:24:11.474592-0700 Car-New[981:120418] Herbie has:
2018-10-28 09:24:11.474825-0700 Car-New[981:120418] a engine
2018-10-28 09:24:11.475023-0700 Car-New[981:120418] a tire
2018-10-28 09:24:11.475049-0700 Car-New[981:120418] a tire
2018-10-28 09:24:11.475058-0700 Car-New[981:120418] a tire
2018-10-28 09:24:11.475065-0700 Car-New[981:120418] a tire
Program ended with exit code: 0
访问方法的说明已经被@property声明所取代,也没有了实例变量name的声明,主函数中的点表示法。
2.1名称的使用
- 属性的名称石中玉属性的实例变量名称相同,这种情况非常普遍
- 不过有时候,你也希望实例变量是一个名称,而公开的属性是另一个名称
只需要在@interface Car : NSObject中修改该实例变量的名称
@interface Car : NSObject{
NSString *appellation;
}
@property (copy) NSString *name;
@end
再修改@synthesize指令
@synthesize name = appellation;
- 编译器将创建-setName和-name方法,但在实现代码中用的却是appellation实例变量
2.2只读属性
- 默认情况下,属性是可变的(你可以读取也可以修改,readwrite,一般不写)。
- 如果你想要你的属性只可以被读,不可以被修改,你可以对这个@property使用readonly特性
//鞋码和驾驶证号码
@interface Me : NSObject{
float shoesSize;
NSString *licenseNumber;
}
@property (readonly) float shoesSize;
@property (readonly) NSString *licenseNumber;
@end
- 当编译器知道这个@property属性是可读的,它将只生成getter方法而不会生成setter方法
2.3自己动手有时更好
- 我自己来
如果你想要你的属性不要生成任何代码或者创建相应的实例变量,你可以用关键字@dynamic
//添加bodyMassIndex-身体质量指数属性
@property (readonly) float bodyMassIndex;
@dynamic bodyMassIndex
- (float) bodyMassIndex{
//compute and return bodyMassIndex
}
上面的例子就是告诉编译器不要创建变量或者getter方法---我们自己来
- 我不喜欢这个方法名