从C/C++到Objective-C(四)--- 属性的使用

如果一个类含有很多的实例变量,那么我们将会为这些实例变量编写几乎是一样的setter和getter方法,对于写程序来说这样的重复编码当然是不合理的。所以在oc 2.0中苹果引入了属性,它组合了新的预编译指令和新的访问器语法,可以很好的解决编写实例变量访问方法的问题。

我们来看一个实际的例子,类AllWeatherRadial包含了两个float类型的实例变量,如果没有使用属性的话它的接口代码是这样的:

@interface AllWeatherRadial : Tire
{
    float rainHandling;
    float snowHandling;
}

-(void) setRainHandling:(float) rainHandling;
-(float)rainHandling;
-(void) setSnowHandling:(float) snowHandling;
-(float) snowHandling;
@end;

这样的声明了接口后,在类的实现部分我们将会把实例变量rainHandliing和snowHandliing的访问方法都实现出来,这样写的确挺繁琐的。

接下来我们来看看使用属性的接口代码:

@interface AllWeatherRadial : Tire
{
    float rainHandling;
    float snowHandling;
}
@property float rainHandling;
@property float snowHandling;
@end // AllWeatherRadial
这样的写法就要比原来的方法好多了,最起码少敲了很多代码。这里的@property是一种新的编译器功能,它意味着声明了一个新的对象的属性。 而@property预编译指令的作用就是自动声明属性的setter和getter方法; @property float rainHandling;语句表明AllWeatherRadial类的对象具有float类型的属性,其名称为rainHandling。同时你也可以通过调用-setRainHandling:来设置属性,调用-rainHandling来访问属性。在这里属性的名称与实例变量的名称是一样的,当然我们也可以让它们不一样。稍后再讨论这个问题。

接下来我们看看使用属性后的类实现代码:

@implementation AllWeatherRadial

@synthesize rainHandling;
@synthesize snowHandling;
@end;
这里我们只讨论类的访问方法,所以类的其他代码并没有贴出来,类AllWeatherRadial的初始化方法和其他的一些方法都是一样的。这里的@synthesize也是一种新的编译器功能,它表示创建了该属性的访问代码。比如:@synthesize rainHandling;这行代码表示编译器将添加rainHandling实例变量的访问方法。注意这里我们是把实例变量的声明放在了头文件得接口代码中,当然如果你不声明这些变量(就是上面接口代码大括号中的部分)编译器也会声明的。实例变量的声明我们可以放在两个地方:头文件和实现文件中,甚至还可以拆开来一部分放在头文件中声明,一部分放在实现文件中声明。但是这样有什么区别呢? 情况是这样的,如果你声明的类有一个子类,并且想要直接从子类中通过属性来访问变量,那么变量的声明就必须放在头文件中。如果你确定变量只属于当前类则可以把其声明放在实现文件中。如下:

@implementation AllWeatherRadial
{
    float rainHandling;
    float snowHandling;
}

@synthesize rainHandling;
@synthesize snowHandling;
@end;
上面我们说了属性的名称可以实例变量的名称不一样,那应该怎么实现呢?如下代码所示:

@interface AllWeatherRadial : Tire
{
    float rainHandling;
    float snowHandling;
}
@property float propertyName;  //注意这里
@property float snowHandling;
@end // AllWeatherRadial

//在实现文件中也要做相应的修改
@synthesize propertyName = rainHandling;
这样修改了后编译器仍将创建-setPropertyName:和-propertyName方法,但在其实现代码中用的却是rainHandling实例变量。

接下来我们来看看属性中的点表达式,我们都知道在C或者C++中可以直接通过点(.)来访问struct结构体中的变量,而且C++中对象还可以直接通过点(.)来访问对象中的方法。而在OC中的点(.)表达式只需明白一点,OC中的点表达式是为了更好的简化代码而采用的新特性,点表达式实质上是在调用实例变量的访问方法。如果点表达式出现在等号(=)的左边,那么该变量的setter方法将被调用,如:tire.rainHandling = 20.0;实际上是在调用tire对象中的-setHandling:方法。如果点表达式出现在了等号的右边,则调用的是变量的getter方法,如:float number = tire.rainHandling;实际上是调用了tire对象的-rainHandling方法。关于点表达是在使用的时候还有一些需要注意的地方,我们接着往下看。

在前面我们讨论的是float类型的属性,这样的使用方法也适用于int char BOOL和struct等类型。但是当我们在使用访问方法来访问变量的时候需要保留和释放对象,而且还有一些对象,比如字符串的值我们想要复制(-copy)它们。我们先看下面的代码:

@class Tire;
@class Engine;

@interface Car : NSObject
{
    NSString *name;
    NSMutableArray *tires;
    Engine *engine;
}
@property (readwrite, copy) NSString *name;
@property (readwrite, retain) Engine *engine;

- (void) setTire: (Tire *) tire
         atIndex: (int) index;
- (Tire *) tireAtIndex: (int) index;

- (void) print;

@end // Car

在OC的代码中我们经常都会看到在@property 声明属性那里加了一些相关的特性,这些特性是用来限制属性的。比如这里的name属性就添加了readwrite和copy特性,engine加了readwrite和retain特性,在这里readwrite特性表明编译器会为变量name生成setter和getter方法,当然如果是加了readonly 就只会生成getter方法了。我们重点要说的是这里的copy和retain特性,如果不写的话默认的会是assign特性。

那么这里的copy retain assign都有些什么区别呢?首先assign特性是默认的,指定setter方法采用简单的赋值,比如对一些float int 变量是没有问题的,因为只是简单的赋值所以不会更改变量对应的引用计数,所以assign:就是简单赋值,不更改索引计数(Reference Counting).使用assign: 对基础数据类型 (NSInteger)和C数据类型(int, float, double, char,等),如果变量是对象指针的话就不行了。retain和copy在内存管理中说过它们会自动增加引用计数(+1)。retain:释放旧的对象,将旧对象的值赋予输入对象,再提高输入对象的索引计数为1 ,使用retain: 对其他NSObject和其子类 ,retain,是说明该属性在赋值的时候,先release之前的值,然后再赋新值给属性,引用再加1。copy:指定应该使用对象的副本(深度复制),前一个值发送一条release消息。基本上像retain,但是没有增加引用计数,是分配一块新的内存来放置它。copy是创建一个新对象,retain是创建一个指针,引用对象计数加1。copy: 建立一个索引计数为1的对象,然后释放旧对象,copy是创建一个新对象,retain是创建一个指针,引用对象计数加1。当然还有一些其他的特性,如:atomic,nonatomic等,指定原子和非原子特性,同样默认是atomic。retain的做法就类似这样:

-(void) setEngine:(Engine *)newEngine
{
    [newEngine retain];
    [engine release];
    engine = newEngine;
}
对于对象的属性在使用的时候应该特别注意一点,比如在上面接口代码中name属性,name = @"Car" 和self.name = @“Car” 这两个语句有什么区别呢?在使用的时候如果我们用的是前者,也就是没有self点表达式的,那么编译器会认为我们是在给一个名为name的变量进行简单的赋值,当然引用计数是没变的。而采用的是或者带self的语句的话,就表明我们是在调用name的setter方法,也可以写成这样[self setName: @"Car"],这就是点点表达式的妙用。所用在具体使用的时候我们也特别注意一下。还有比较重要的一点:别忘了内存管理,在-dealloc方法中该释放的得记得释放。


参考书籍:《Objective-C 基础教程(第二版)》



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值