Objective-C 编程语言官网文档(三)-如何定义类
如何定义类
大多数面对对象编程都包含为新对象编写代码---定义一个新类。在Objective-C中,类被定义为两部分:
一个接口 声明有一些方法以及类的属性,以及它的父类的名字
一个实现类 真正的类 (包括实现的方法的代码)
下面的几部分通常在它自己的文件里。然而,有是有,一个类的定义可能通过类别的使用跨越几个文件。类目跨越划分类的定义或者扩展一个已经存在的。类别的描述跨越参考 “Categories and Extensions.”
源文件
通常类的接口跟实现是在两个不同的文件中,即便编译器没有强制要求。接口文件必须对任何想要使用该类的人可用。
一个文件可以声明或者实现不止一个类。不过按照惯例,通常每个类都有一个单独的接口文件,以及一个单独的实现文件。保持类接口的独立能够更好的反映它们作为独立实体的状态。
接口和实现文件都用该类命名。实现类文件的名字的扩展名是.m, 指明它含有Objective-C 源码. 接口文件可以使用任何别的扩展。因为它是被包含在源文件中的,接口文件通常使用典型的头文件所使用的.h 扩展名。例如, Rectangle 类可以定义在Rectangle.h 以及Rectangle.m中。
让一个对象的接口与实现独立开来的做法很好的符合了面向对象设计的思想。一个对象是一种自包含的实体,它可以从外部被当做类似黑盒一样访问。一旦你决定你的对象如何与你程序中的其它元素进行交互,也就是说,一旦你声明好它的接口,你就可以自由的修改它的实现,而不会影响到应用的其它部分了。
类接口
类接口的声明是以编译指令@interface 开头并以指令@end结尾。 (所有的Objective-C 编译器指令都是以 “@” 开头.)
@interface ClassName : ItsSuperclass
// Method and property declarations.
@end
第一行声明标示一个新类名并将其与它的父类联系起来。父类定义了新类在继承树中的位置,就像我们在“Inheritance.” 中讨论的那样。
类的方法以及属性定义在之后,类声明结尾处的前面。能被类对象使用的方法名,即类方法,方法前面会有个加号标记;
+ alloc;
为类的实例使用的方法,即实例方法,方法前会有一个减号标记。;
- (void)display;
尽管这不是什么惯例,你也可以给类方法和实例方法定义一样的名字。方法也可以跟实例变量名字相同,这更常见,尤其是当方法在变量中返回值时。例如, Circle 有一个 radius 方法与 radius 实例变量相匹配。
方法的返回值类型的名声使用的是标准的C语法:
- (float)radius;
参数的类型定义也是一样的:
- (void)setRadius:(float)aRadius;
如果一个返回值或者参数类型没有显示的被声明,那么它会假定默认类型为id。早前讲到过的 alloc 方法会返回 id.
当有更多的参数时,所有的参数会声明在方法名内,在冒号的后面。例如:
- (void)setWidth:(float)width height:(float)height;
有较多参数的方法,在声明这些参数时使用逗号以及省略号,就像函数那样:
- makeGroup:group, ...;
属性声明的格式是:
@property (attributes) Type propertyName;
更多关于属性声明的信息可以参考 “Declared Properties.”
注意: 过去,接口还要求类的实例变量的声明,数据构造器是每个类实例的一部分。它们定义在 @interface 声明后面的大括号离,方法声明之前。
@interface ClassName : ItsSuperclass
{
// Instance variable declarations.
}
// Method and property declarations.
@end
实例变量代表着实现细节,通常不能被类自身意外访问。并且,你可以在实现块来声明或者使用声明的属性来综合它们。通常你不应该这么做,把实例变量声明在公共接口中,因此你应当忽略这个大括号。
导入接口
接口文件必须被包含在任何依赖这个类接口的源码模块中,即任何需要为这个类创建实例的模块,发送一个消息来调用为这个类声明的方法,或者使用定义在类中的实例变量。接口通常包含在 #import 指令中
#import "Rectangle.h"
这个指令跟 #include的作用是一样的,唯一的区别是这条指令可以确保同一个文件不会被导入多次。因此在Objective-C的基础文档中的例子大多使用这个指令。
为了反映一个类的定义是建立在所继承的类的定义基础上,那么这个接口文件就以导入父类的接口开始。
#import "ItsSuperclass.h"
@interface ClassName : ItsSuperclass
// Method and property declarations.
@end
这个惯例说明,每个接口文件都间接的包含所有继承的类的接口文件。当一个源文件模块导入一个类接口时,它同时也获得了整个接口树中的接口。
如果有一个 precomp—一个预编译头文件—支持父类,你可能会更愿意导入预编译的文件。
引用其它类
当一个接口文件声明了一个类,导入了它的父类,显示包含了所有继承到的类的声明,从NSObject 开始一直到它的父类。如果接口引用了不在它继承树中的类,它就必须显示的导入它们或者用@class 指令声明它们
@class Rectangle, Circle;
这个指令简单的通知编译器, “Rectangle” 跟 “Circle” 是类名,没有导入它们的接口文件。
在接口文件中,当它静态的给实例变量,返回值以及参数赋予类型时,用到了类名
- (void)setPrimaryColor:(NSColor *)aColor;
引用了 NSColor 类.
因为向这样声明只是简单将类名用作赋予类型,并不依赖任何接口的详情 (它的方法和实例变量), @class 指令已经可以为编译器提供足够的有关编译器能够获得什么信息预警。但是,当类的接口真正使用时(实例创建时,发送消息时), 此时类接口就必须被导入。通常接口文件会使用 @class 指令来生命类, 而真正的响应的实现文件会导入它们的接口(因为它需要为这些类创建实例并给它们发消息)。@class 指令将给编译器以及链接器看的代码量最小化 , 同时也是最简单的方式来提供类名的前置声明。让事情变的简单,它可以有效避免导入的文件本身还导入了别的文件时可能出现的潜在问题。例如,当一个类声明了两外一个类的一个静态类型的实例变量,并且他们的两个接口文件相互导入,这种情况下可能两个类都无法正确编译。
接口的职责
接口文件的职责是为其它源代码模块(以及其它程序猿)声明新类。包含它们的类协同工作需要的一些信息 (程序猿可能比较喜欢还有点文档).
接口文件告诉用户类是怎么接入继承树的,以及需要用到的其它的类,继承的或者在类中某个位置引用到的。
通过它的方法声明列表,接口文件让其它模块知道什么样的消息可以发送给类对象以及类的实例。每个能在类声明外部使用的方法都声明在接口文件中。实现类内部使用的方法可以忽略