1-objectc-定义类

来源:互联网 发布:srt mac 乱码 编辑:程序博客网 时间:2024/05/01 04:18

原文链接URL

博客目录链接

定义类

当你为 OS X 或 iOS 编写应用,你的大部分时间都在和对象打交道。Objective-C中的对象和其他面向对象的编程语言一样:他们通过相关的行为包装数据。

一个应用被作为一个大型的相关对象系统进行构建,这些对象相互通讯以解决特定问题,比如显示一个虚拟接口、响应用户输入或存储信息。对于OS X 或 iOS 开发,你不需要从头开始创建对象来解决每一个可以想象的问题,而是你有一个大型的已存在对象库可供你使用,这是由Cocoa (针对 OS X) 和 Cocoa Touch (针对 iOS)提供的。

这些对象中的一些可以立即使用,例如基本数据类型如strings 和数字,或数字接口元素如按钮和表格视图。有些事为你设计通过你自己的代码自定义,从而按照你需要的方式运行。应用开发过程包括确定如何最佳的自定义或合并潜在框架所提供的对象和你自己的对象,从而让你的应用具备特定的功能和特点。

在面向对象术语中,一个对象就是一个类的实例。本章节展示了如何在Objective-C 中通过声明接口定义类,所谓类描述了你希望类和它的实例被使用的方式。 接口包括该类可以接受的信息列表,所以你同时需要提供类的实现,实现中包含了针对每个消息的响应中执行的代码。

类是对象的蓝图

一个类描述了任何特定对象类型的通用行为和属性。对于一个string 对象 (在 Objective-C 中,这是一个 classNSString 的实例),该类提供了多种方法来检查和转换它所代表的内部字符。类似的,用来描述数字对象的 (NSNumber) 围绕一个内部的数值提供功能,例如转换该值到不同的数据类型。

类似的,来自相同蓝图的多个构建在结构上是一样的,每个类的实例都和该类的其他实例分享了相同的属性和行为。每一个 NSString 实例都有相同的行为,而与内部保存的字符串无关。

任何对象都被设计以特定的方式运行。你可能知道一个字符对象代表了一个字符串,但你不需要知道用来存储这些字符的内部机制。你不知道任何关于该对象操作字符的内部行为,但你需要知道如何与对象互动,可能是向它请求特定字符或请求一个新的对象其中所有内部字符串均被转换为大写。

在 Objective-C中,类的接口明确指定了一个给定对象类型如何被其他对象使用,换句话说,它定义了类实例和外部世界的公共接口。

可变性决定是否一个所代表的值可以改变

一些类定义不可变对象。这意味着内部内容必须在一个对象被创建时设置,无法在后面被其他对象更改。在Objective-C中,所有基本 NSStringNSNumber 对象都是不可变化的。如果你需要代表一个不同的值,你必须使用一个新的NSNumber实例。

一些不可变类同样提供一个可变版本。如果你特别需要在运行期更改一个字符串的内容,比如添加一个通过网络连接获得的字符串,你可以使用 NSMutableString 类的实例。该类有点类似NSString 对象,但他们同时提供函数对象所代表的值。

尽管NSString 和NSMutableString是不同的类,但他们有很多相似点。不是从头写两个完全不同的类来实现许多相类似的行为,这里使用了继承。

从其他类继承获得的类

在自然世界中,分类将动物按照特定条款进行分类,例如物种、属、家族。这些组是分层的,例如多个物种都属于一个属,多个属都属于一个家族。

例如人类、猩猩和大猩猩都有许多明显的相似。尽管他们属于不同的物种,甚至是不同的属、 族和子家族。他们在分类学上相关,因为他们都属于相同的家族 (被称为“人科”),如下表 Figure 1-1.

Figure 1-1  物种间的亲属关系

在面向对象编程的世界中,对象也同样按层次分组。不是为不同层次分类使用不同的术语,如属或种,对象被简单的分为类。和人类继承的原始人类家族成员的某些特征一样,类可以从其父类继承功能。

当一个类继承另一个类,子类继承所有在父类中定义的行为和属性。同样也可以定义自己的额外行为和属性、或复写来自父类的行为。

在Objective-C 字符类中, NSMutableString 的类定义指定继承 NSString 如图 Figure 1-2。所有被 NSString提供的功能在NSMutableString中均有效,如查询指定字符串或请求新的大写字符串,但 NSMutableString 增加了方法允许你追加、插入、替换或删除子字符串或特定字符。

Figure 1-2  NSMutableString 类继承

根类提供基本功能

和所有生物共享一些基本的“生命”特征一样,在Objective-C中一些功能是所有对象共有的。

当一个 Objective-C 对象需要和一个其他类的实例一起工作时,需要其他类提供了一定的基本特征和行为。由于这个原因,Objective-C 定义了一个根类,绝大多数类都必须继承,被称为 NSObject。当一个对象遇到另一个,期待能够互动使用至少NSObject类中定义的基本行为。

当你定义自己的类时,你必须至少继承 NSObject。一般来说,你必须找到一个 Cocoa 或 Cocoa Touch 对象, 来提供最接近你需要的功能,并继承它。

当你希望自定义一个按钮在一个 iOS 应用中使用,比如,而提供的 UIButton 类没有提供足够的可定义属性来满足你的需要,那么创建一个新的类并继承UIButton要比继承NSObject来的更有意义。如果你简单的从 NSObject 继承,你就需要重复定义UIButton 类中已经定义的所有可视化互动与交互,让你的按钮按照用户希望的方式运行。更重要的是,继承UIButton,你的子类自动获得未来任何针对UIButton函数的功能增强或bug修正。

 UIButton类自身被定义为从 UIControl继承,这个类描述了iOS上所有用户接口控制的基本行为。UIControl 类又继承于UIView,提供在屏幕上展示对象的基本功能。UIView继承于UIResponder,从而允许对用户输入做出反应,例如点击、手势和摇晃。最后UIResponder 继承于 NSObject,如下图 Figure 1-3.

Figure 1-3  UIButton 类的继承

这个继承关系图说明任何 UIButton 的子类都会继承不仅仅在 UIButton 中定义的功能,更要继承在其每个超类中定义的功能。你最终获得一个类的对象可以像一个按钮那样运行,可以在屏幕上显示,对用户输入做出反应,与其他基本 Cocoa Touch 对象交互。

牢记这个继承关系对于你需要使用的任何类都非常重要,这让我们实现自己需要的功能。 Cocoa 和 Cocoa Touch的类参考文档已经提供,比如,允许简单的从任何类导航到其超类。如果你无法在一个类接口或参考中找到任何你需要寻找的,它可能在关系中更靠后的超类中被定义或记录。

类定义的接口所预期的交互

面向对象编程的好处之一就是预先定义——所有你需要知道的以便使用一个类的信息,即何如与实例交互。更明确的说,就是一个对象的设计应该隐藏其内部的实现细节。

如果你在一个iOS应用中使用 UIButton,举例,你不需要关心像素是如何操作的,按钮会自己显示在屏幕上。你需要知道的只是你可以更改特定的属性,比如按钮的标题和颜色,并且当你将其添加到可视化接口时确信它将正确显示在屏幕上并按照你希望的方式运行。

当你定义自己的类,你需要搞清楚这些公共属性和行为才能开始。什么属性你希望别人能够公开访问?你应该允许这些属性被修改吗?其他对象如何与你的类实例交互?

对于你的类来说,这些信息深入接口——接口定义了你希望别的对象如何与你的类实例交互。公共接口和类的内部行为分别描述, 从而实现类。在 Objective-C 中,接口和实现通常在不同的文件中,所以你只需要让接口公开。

基本语法

Objective-C 语法如下描述类的接口:

@interface SimpleClass : NSObject
 
@end

这个例子说明类名为 SimpleClass,继承于NSObject。

公共属性和行为定义在 @interface 定义中。在该例子中, 除了超类没有指定任何东西,因此 SimpleClass实例的功能只会从 NSObject 继承。

一个对象值的属性访问控制

类通常会有属性需要公开访问。如果你在一个记录应用中定义一个类代表人,举例,你可能决定需要 strings 属性来代表一个人的姓名。

这些属性的定义必须添加在接口中,例如:

@interface Person : NSObject
 
@property NSString *firstName;
@property NSString *lastName;
 
@end

在该例子中, Person 类定义两个公开属性,均为 NSString 类的实例。

这些属性均为 Objective-C 对象,因此他们使用 星号 来暗示它们是 C 指针。 和其他在 C 中定义的变量一样,还需要在结尾增加一个封号。

你可能决定添加一个属性代表人的出生年,以便对人按年龄组排序而非姓名。你可以使用一个数字对象。

@property NSNumber *yearOfBirth;

但这样可能有点浪费,如果只是存放如此简单的一个数值。另一个可选方法是使用 C 提供的基本类型,来保持标量值,如整形。

@property int yearOfBirth;

特征属性暗示数据访问权限以及存储注意事项

到目前为止,该例子展示的所有声明属性都为公开访问权限。这意味着其他对象可以同时读写该属性值。

在一些情况下,你可以决定声明一个属性不允许更改。在实际生活中,一个人必须填写大量文件来变更姓名。如果你在填写一个官方记录应用,你可能选择将人的姓名定义为只读,提交更改时,需要向负责验证、批准和拒绝请求的中介对象发起请求。

Objective-C属性声明可以包含 特征属性,用来暗示其他东西,是否只读。在一个官方记录应用中,Person 类接口可能是如下设计:

@interface Person : NSObject
@property (readonly) NSString *firstName;
@property (readonly) NSString *lastName;
@end

特征属性在 @property 关键词后,完整列表参见 “Declare Public Properties for Exposed Data.”

方法声明暗示一个对象可以获得的消息

到目前为止,该例子已经包含了一个类描述一个典型的模型对象,或一个被设计封装数据的对象。在 Person 类的例子中,可能只需要能访问被声明属性的函数。但是,类的主体,对于任何被声明的变量包含函数。

Objective-C 软件是从大量对象中构建的,必须注意,这些对象可以通过发送消息互相影响。在 Objective-C 规范中,一个对象通过调用其他对象的函数给相应的对象发送消息。

Objective-C 方法和标准 C和其他编程语言 中的方法在概念上是相似的,尽管语法上有些不同。一个 C 函数定义如下:

void SomeFunction();

相同的 Objective-C 方法定义如下:

- (void)someMethod;

在该例子中,方法没有参数,声明开头的C void 关键字用来暗示该方法完成时不返回任何值。

方法名前的减号 (-) 暗示这是一个实例方法,可以被类的任何实例调用。这用来区别类的方法,只能被类自己调用,参见 “Objective-C Classes Are also Objects.”

和C的函数原型一样,在一个 Objective-C 类接口中的方法原型需要像其他C语句一样需要一个封号结束。

方法可以带参数

如果你需要声明带有一个或多个参数的方法,语法与典型的C函数不同。

对于一个 C 函数,参数被指定在圆括号中:

void SomeFunction(SomeType value);

一个Objective-C方法定义包含参数作为名字的一部分并使用冒号,如下:

- (void)someMethodWithValue:(SomeType)value;

同样的,返回类型中,,参数类型被定义在圆括号中,类似 C 中的类型强制转换。

如果你需要提供多个参数,语法也与C不同。 C 函数的多个参数被指定在圆括号中,用逗号分隔;在 Objective-C中,带两个参数的方法声明如下:

- (void)someMethodWithFirstValue:(SomeType)value1 secondValue:(AnotherType)value2;

在这个例子中,value1 和 value2 是实现中使用的名字,当方法被调用时用来访问所提供的值,如果它们是变量的话。

有些编程语言允许被称为命名参数的功能定义;值得注意的是在 Objective-C中不是这样。方法调用时参数的顺序必须匹配方法定义,实际上secondValue: 部分声明是方法名称的一部分:

someMethodWithFirstValue:secondValue:

这是帮助让 Objective-C 具有更高可读性的功能,通过方法调用所传递的值被指定为内联,在相关函数名边上,参见 “You Can Pass Objects for Method Parameters.”

注意: value1value2 在上面指定的变量名不是函数定义的一部分,这意味着不需要在定义和实现中使用相同的变量名。只需要前面匹配,也就是说,你必须保持方法名以及参数和返回值类型相同。

作为一个例子,该方法和上面有相同的签名:

- (void)someMethodWithFirstValue:(SomeType)info1 secondValue:(AnotherType)info2;
这些方法和上面的有不同的签名:
- (void)someMethodWithFirstValue:(SomeType)info1 anotherValue:(AnotherType)info2;
- (void)someMethodWithFirstValue:(SomeType)info1 secondValue:(YetAnotherType)info2;

类名必须唯一

值得注意,每个类的名字在同一个应用内必须唯一,即使跨越include进来的框架或库。如果你尝试创建一个新的类与一个已有的类同名,你将获得一个编译器错误。

因此,建议给任何你定义的类增加前缀,使用三个或更多字符。这些字符应该与你当前编写的应用相关,或与一个框架名相关、或仅仅是你的名字。

本文余下部分使用的雷鸣前缀如下:

@interface XYZPerson : NSObject
@property (readonly) NSString *firstName;
@property (readonly) NSString *lastName;
@end

历史备注:如果你好奇为何你遇到的如此多的类都有一个NS前缀,这是因为 Cocoa 和 Cocoa Touch的历史原因。Cocoa 刚开始作为收集框架用于构建NeXTStep操作系统的应用。当苹果于1996 年购买 NeXT , 大部分 NeXTStep 被包含到 OS X,包括已有的类名。 CocoaTouch 被介绍为 iOS 上的 Cocoa;有些类在Cocoa 和 Cocoa Touch均可使用,但仍有大量的类对于特定系统是唯一的。

两个字母的前缀 NSUI ( iOS 上的用户接口元素) 被苹果保留使用。

相对比的,方法和属性名只需要在他们定义的类里唯一。尽管在一个应用中的每个C函数必须有唯一的名字。多个Objective-C类拥有相同的函数名也是可以接受(常常让人满意)。在相同的类定义中,你不能定义一个方法超过一次,尽管当你希望重载一个从父类继承的方法时,必须使用与原来定义相同的名字。

在方法中,一个对象的属性和实例变量 (详见 “Most Properties Are Backed by Instance Variables”) 需要在他们定义的类中唯一。但是如果你使用全局变量,它们必须在应用或项目中唯一。

更多命名惯例和建议参见 “Conventions.”

类的实现提供它的内部行为

一旦你为类定义了接口,包括属性和公共访问的方法,你需要编写代码实现对应的行为。

和先前开始的一样,类的接口被通常放在一个特定文件中,常常是一个头文件,通常拥有后缀名.h。为 Objective-C 类编写的实例包括一个源文件拥有后缀名.m。

当在头文件中定义了接口后,你将需要告诉编译器在尝试编译源文件的实现前读取它。Objective-C 提供一个预编译器指令#import完成该任务。这和C的 #include 指令相同,但确保文件在编译中只包含一次。

注意,预编译器指令与传统 C 语句不通,没有使用封号。

基本语法

提供类的实现的基本语法如下:

#import "XYZPerson.h"
 
@implementation XYZPerson
 
@end

如果你在类的接口中声明任何方法,你将需要在文件中实现它们。

实现方法

对于一个简单的拥有一个函数的类接口,如下:

@interface XYZPerson : NSObject
- (void)sayHello;
@end

实现将如下:

#import "XYZPerson.h"
 
@implementation XYZPerson
- (void)sayHello {
    NSLog(@"Hello, World!");
}
@end

该例子中使用 NSLog() 记录信息到终端。类似C标准库中 printf() 功能,并使用一个参数变量,第一个必须为 Objective-C 字符。

和C函数定义的方法实现相同,需要在括号中包含相关的代码。更多的是,方法名必须与原型相同,参数和返回类型必须完全一致。

Objective-C从 C 中继承了大小写敏感,所以方法:

- (void)sayhello {
}

将被编译器视为与 sayHello不同。

一般来说,方法名必须开始于小写字母。Objective-C 惯例使用比典型c函数更多的描述性名字。如果一个方法名包含多个文字,使用大小写间隔 (大写每个单词的首字母)来方便阅读。

同时注意,空白在 Objective-C 中很灵活。每行的缩进可以使用空格或制表符,你将经常看到单独一行中放置一个括号,如下:

- (void)sayHello
{
    NSLog(@"Hello, World!");
}

Xcode,苹果的集成开发环境(IDE) 用来创建 OS X 和 iOS 软件,将自动缩进你的代码,基于自定义的用户首选,参见<Xcode Workspace Guide>中的“Changing the Indent and Tab Width”

在下面一章,你将看到更多方法实现的例子, “Working with Objects.”

Objective-C 类也是对象

在 Objective-C 中,一个类自己也是一个不透明类型的对象被称为类。类没有使用前面展示的声明语法定义属性,但他们可以接受消息。

典型使用类的方法是一个工厂方法,一种替代对象分配和初始化过程,参见“Objects Are Created Dynamically.”  比如,NSString 类,拥有一个工厂方法用来创建一个空字符串对象,或一个字符对象使用特定的字符串初始化,包括:

+ (id)string;
+ (id)stringWithString:(NSString *)aString;
+ (id)stringWithFormat:(NSString *)format, …;
+ (id)stringWithContentsOfFile:(NSString *)path encoding:(NSStringEncoding)enc error:(NSError **)error;
+ (id)stringWithCString:(const char *)cString encoding:(NSStringEncoding)enc;

如上述例子,类方法使用+号定义,这些和实例方法不同,实例方法是用- 号。

类方法原型可能被包含在类接口中,就像实例方法原型。类方法使用和实力方法一样的方式实现,包含在代码的@implementation 块中。

 

对于符号的译者说明:

加号 是可以通过类名直接调用这个方法,
而减号则要实例化逸个对象,然后通过实例化的对象来调用该方法!
(+ 和java中的static 方法相似)

 

0 0
原创粉丝点击