第十一章、属性

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.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)它们。而对于其他对象的值,如委托(后面会说)你根本不会想要保留它们。

第十一章、属性_第2张图片

下面给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的声明,主函数中的点表示法。

第十一章、属性_第3张图片

2.1名称的使用

  • 属性的名称石中玉属性的实例变量名称相同,这种情况非常普遍
  • 不过有时候,你也希望实例变量是一个名称,而公开的属性是另一个名称

只需要在@interface Car : NSObject中修改该实例变量的名称

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

再修改@synthesize指令

@synthesize name = appellation;
  • 编译器将创建-setName和-name方法,但在实现代码中用的却是appellation实例变量
第十一章、属性_第4张图片

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方法---我们自己来

  • 我不喜欢这个方法名
    第十一章、属性_第5张图片

2.4特性不是万能的

第十一章、属性_第6张图片

你可能感兴趣的:(第十一章、属性)