Programming with Objective-C——翻译2章

二章 类定义
1、Classes Are Blueprints for Objects类是对象的蓝图
一个类描述了相同类型对象的属性和行为。对于字符串对象(OC里是NSString类的实现),它的类中提供了检验和转换字符串对象的多种方式。比如数值类(NSNumber)提供了与内部数值类型相关的一些功能,如将一种数值类型转换成另外一种类型。
现实中不同建筑可由同一套图纸建造,它们区别仅在于建筑内饰。类似地,同一个类的所有对象都具有相同的属性和行为。所有的NSString类对象都有相同的行为,不管它们内部是什么字符串。任何对象都有其特定用途。你知道一个字符串对象存放的是字符串,但你不必知道它如何去存储串内字符。
你也不用知道这个对象内部是如何操纵字符的,你只需了解如何使用这个对象。比如让它输出字符串,或要求它将内部所有字符转换成大写并存储到一个新对象中。
OC类接口也指明了对象间的交互方式。即:接口定义了两对象之间或是对象同外界之间的交流方式。
2、Mutability Determines Whether a Represented Value Can Be Changed类的易变性
有些类型对象是不可变的。就是说建立这类对象时候必须初始化,之后便不能被修改。OC中,NSString和NSNumber类对象都是不可变的。就是说你必须再次建立一个NSNumber对象来存放与之前不同的数值。但有些不变类也提供了可变版本。当你需要在运行时对一个字符串对象进行修改,如在字符串尾附加网络上接收的内容,你可以使用NSMutableString对象来达到目的。NSMutableString类对象的行为同NSString类对象基本相同,不同之处在于前者内部字符串可在运行时修改。
NSString和NSMutableString有许多相同行为,尽管它们是两个不同的类。比起完全从零开始实现两个具有相似行为的类,更好的选择是使用继承。
3、Classes Inherit from Other Classes类的继承
在自然界中,我们通过分类学的方法将动物进行区分,可以通过种、属、族的不同来进行分类。这些分类是具有层级属性的,比如许多不同的种可能会具有相同的属,又比如不同的属可以有相同的族。(生物学中的词暂时乱的:译注)
例如大猩猩、人类以及猩猩,显然具有许多相同之处。(中文的猩猩和大猩猩貌似没有进行区分:译注)虽然三者是不同种的生物,也是不同的属,不同的部,但是这三者都属相同的族:人科(Hominidae)。如图1-1所示:

在面向对象编程的世界中,对象也是通过层次关系进行分类的。比起在生物学中用到许多不同名词(属、族、种。。。)来描述这种层次关系,编程中的对象组织只是简单地用到了:类。就像生物学中人类可以继承某个人科成员一样,OC中类也可以继承。
当一个类继承了另一个类后,子类便拥有了父类的所有行为和属性。子类中还可定义自己独有的行为和属性,或是覆盖父类的某些行为。
在OC字符串类的例子里,NSMutableString类继承了NSString类,如图1-2所示:

NSString类方法在NSMutableString类中都可用,比如查询特定字符串或请求新的大写字符串对象。另外NSMutableString还加入了其它一些独有的方法,如允许用户附加、插入、替换、删除子串或特定字符。
4、The Root Class Provides Base Functionality根类(基类)
所有生物都具备“生”的特征,而OC中的对象也有一些共同的特征。
当两个对象进行通信时,一方会希望另一方提供某些基本接口。所以,OC里定义了一个基类NSObject(绝大多数类都继承自它)。两个对象交互时,至少用NSObject类里面定义的接口进行交互。
当你自定义类的时候,你的类最小限度上应继承自NSObject。总之,你应从Cocoa或Cocoa Touch中找最贴近需求的类,然后进行继承。
比如你想在iOS应用中创建一个按钮,但系统提供的UIButton不能完全满足要求,那就应该创建一个继承自UIButton的新类来达到要求,而不是继承自NSObject的。如果这个新类继承自NSObject,为满足需要功能,你就要再次完成一遍UIButton已经做好的所有复杂工作。如果通过UIButton继承,你的新类在以后将可以自动获得任何UIButton类上的补丁修复和性能提升。
UIButton类继承自UIControl类,UIControl类描述了iOS上用户界面控制行为的共同特征。UIControl类又继承自UIView类,UIView类用于屏幕显示。UIView类继承自UIResponder类,这个类作用是对用户的输入(按压,手势,晃动)进行响应。最后,这颗继承树到了根节点NSObject,UIResponder类继承自UIObject类,如图1-3所示:

上面的继承链表明,任何UIButton的子类不仅继承了它的功能,而且还继承了UIButton所有祖先的功能。这个继承链最后的结果就是,UIButton类按钮能够工作,也能在屏幕上显示,也能响应用户的输入,并且可以同其它基本的Cocoa Touch类对象进行通信。
记住编程中你用到的类继承链,这样可以准确达到想要的功能。在Cocoa和Cocoa Touch库的类引用文档中可以轻松导航并且查询到任何类及其祖先类。如果你在一个类的接口或引用中不能找到所需的功能,很有可能继承链上它的祖先类具有你需要的功能。
5、The Interface for a Class Defines Expected Interactions定义类的接口进行通信
之前我们就了解,面向对象程序设计最大的优点:你不需要知道类对象的构造和内部运行,你只需要知道如何同对象进行交互。(对象即黑盒:译注)更准确地说,对象在设计时应该是隐藏了其内部实现细节的。
比如你在iOS应用中使用了一个UIButton对象,你不用知道它是如何操纵像素来显示到屏幕上的。你只需知道如何修改对象的属性,比如标题和颜色。当这个对象被添加到界面,就肯定会正确地显示,并且行为和你预想的一致。
在自定义类时,首先应明确这个类的公共属性和行为。比如什么属性是能被外界访问,是否允许这些属性被修改,其它类型对象如何同这个对象交互。
这些信息由接口接收,即定义了其它类对象同自定义类对象的交互途径。自定义类的接口声明和接口定义是分开的,后者构成了类的实现。在OC中,接口声明和实现通常存放在不同文件中,以便于只将类接口公开。
6、Basic Syntax基本语法
OC中类接口声明语法如下所示:
@interface SimpleClass : NSObject

@end
这个例子中声明了SimpleClass类,它继承自NSObject。
类的公共属性和行为在@interface和@end之间定义。在这个例子中,子类没有添加额外的元素,所以SimpleClass类的行为完全继承自NSObject。
7、Properties Control Access to an Object‘s Values类对象的属性控制
对象常拥有公共属性。假如在程序中定义Person类,你可能会需要一个字符串类型的属性用于描述姓名。
属性声明应该包含在类声明内部,如下所示:
@interface Person : NSObject
@property NSString *firstName;
@property NSString *lastName;
@end
这个例子里,Person类中声明了两个公共属性,都是NSString类型。
两个属性都是OC对象,所以能加星号表示它们是对象指针。由于是声明语句,和C中任何的变量声明类似,所以末尾有分号。
你可以决定再加入一个属性来描述一个人的出生日期,这样除了按姓名之外,你还可以按年龄对这些人进行分组。可以使用一个数值对象:
@property NSNumber *yearOfBirth;
但使用数值对象存储生日就太大材小用了。一个解决方案是使用C里面提供的原子类型来存储数值,比如一个int:
@property int yearOfBirth;
8、Property Attributes Indicate Data Accessibility and Storage Considerations属性修饰
上面例子里声明的属性具有完全公共访问权限的。也就是说其他对象可以对属性进行自由读写。
在某些情况下,你可能并不愿某些属性被轻易修改。如同在现实世界中,一个人想改名的话,需要准备许多材料才行。如果你正在写一个公民身份登记程序,可将属性中的人名设置成只读,任何修改人名的请求都必须经过另一个中间对象,由这个中间对象来验证请求,并决定是否允许修改。
OC属性声明中可以包含属性修饰,用于指定一个属性是否只读。在实际中,Person类的接口可以这样声明:
@interface Person : NSObject
@property (readonly) NSString *firstName;
@property (readonly) NSString *lastName;
@end
属性修饰在@property关键字后面的括号内指定,详见Declare Public Properties for Exposed Data。
9、Method Declarations Indicate the Messages an Object Can Receive对象方法声明
上面的例子介绍的都是简单对象模型,只讲到了数据的封装。在Person类的例子里,除了访问两个属性,可能并不需要其它功能。但是类的本质,却是对类的任何属性进行操作。
前面谈到OC软件都是以大量对象为基础建立起来的,必须认识到对象之间存在消息传递。在OC中,对象间消息传递是通过方法调用实现的。
OC方法在概念上与C和其它编程语言中的函数类似,但在语法上并不相同。一个C函数声明是这样的:
void SomeFunction();
与之等价的OC方法声明是这样的:
- (void)someMethod;
上面的method没有参数。关键字void被放在括号内,表示这个方法无返回值。
返回类型前的减号“-”表示这是一个对象方法,即它可以被这个类的对象调用。与之相对的是类方法,二者区别在于类方法可以被类本身调用,详见Objective-C Classes Are also Objects。
和C中的函数原型声明一样,OC中方法声明最后也要加上分号“;”。
10、Methods Can Take Parameters方法的参数
如果你需要声明一个带参方法,声明的语法和C函数不一样。
对于C函数,参数在括号内指定,如下所示:
void SomeFunction(SomeType value);
OC中将方法参数作为方法名的一部分,以冒号分隔,如下所示:
- (void)someMethodWithValue:(SomeType)value;
- (void)someMethodWithFirstValue:(SomeType)value1 secondValue:(AnotherType)value2;

someMethodWithFirstValue:secondValue:

-(void)someMethodWithFirstValue:(SomeType)info1 secondValue:(AnotherType)info2;

-(void)someMethodWithFirstValue:(SomeType)info1 anotherValue:(AnotherType)info2;
-(void)someMethodWithFirstValue:(SomeType)info1 secondValue:YetAnotherType)info2;

11、Class Names Must Be Unique类名必须唯一
@interface XYZPerson : NSObject
@property (readonly) NSString *firstName;
@property (readonly) NSString *lastName;
@end

12、The Implementation of a Class Provides Its Internal Behavior类的实现
12.1、Basic Syntax类实现中的基本语法
#import "XYZPerson.h"
@implementation XYZPerson
@end

12.2、Implementing Methods方法的实现
@interface XYZPerson : NSObject
- (void)sayHello;
@end

#import "XYZPerson.h"
@implementation XYZPerson
- (void)sayHello {
NSLog(@"Hello, World!");
}
@end
</pre><pre name="code" class="objc"><pre name="code" class="objc">- (void)sayhello {
}
- (void)sayHello{
NSLog(@"Hello, World!");
}

13、Objective-C Classes Are also Objects 类也是对象
+ (id)string; //构造空串
+ (id)stringWithString:(NSString *)aString; //构造一个存放aString的串
+ (id)stringWithFormat:(NSString *)format, …; //构造一个指定格式字符串
+ (id)stringWithContentsOfFile:(NSString *)path encoding:(NSStringEncoding)enc error:(NSError **)error; //存放文件内字符串?
+ (id)stringWithCString:(const char *)cString encoding:(NSStringEncoding)enc; //存放C串?

14、Exercises练习:

(NS代表的是NeXTSTEP,是Jobs在1985年离开水果的时候创建的公司名称。)


当编写OSX或iOS程序时,你多数情况都是在和对象打交道。OC中的对象和其它面向对象语言中的一样:对象是数据和相关操作的封装。

一个程序由许多相互关联的对象组成,它们之间进行通信,共同解决一个特定问题。比如显示一个虚拟接口,响应用户输入,存储信息等。对于OSX和iOS开发,你不需要从零开始创建各种对象,因为已经有一个庞大的对象库可供使用,这个库由Cocoa(OSX平台)或Cocoa Touch(iOS平台)框架提供。

有些对象可直接使用,如字符串或数字等基本数据类型,以及按钮和表格等用户界面元素。另外一些则由你在其中编写代码,以实现需要的功能。开发中你应保证自定义对象最优化,决定框架对象和自定义对象如何组织,从而让你的程序具有独特的界面和功能。

在面向对象程序设计中,对象是类的实例化产物。本章中自定义了一个单接口类XYZPerson,外界通过接口来使用类及其对象。接口包含类能够接收的消息列表,所以你还要提供类的实现,即消息处理代码。

和返回类型一样,参数类型在括号内指定,后面跟上参数名,类似于C中的变量类型转换。

如果你需要多个参数,那么语法和C中的更不一样了。C里面的多个参数是在括号内指定,并且由逗号分隔;而在OC中,多个参数的语法如下所示:

在上面的代码中,value1和value2是两个形参名,当方法被调用时,这两个参数接收传入的实参。

有的编程语言中允许使用命名参数,但在OC中没有。调用方法的格式必须和方法声明中的形式一致,实际上,上面代码中secondValue也是方法名的一部分。

这样提升了OC代码的可读性,详见You Can Pass Object for Method Parameters。

Note: The value1 and value2 value names used above aren’t strictly part of the method declaration, which means it’s not necessary to use exactly the same value names in the declaration as you do in the implementation. The only requirement is that the signature matches, which means you must keep the name of the method as well as the parameter and return types exactly the same.

注意:上面代码中value1和value2这两个名字并非方法声明的一部分,也就是说你在方法实现中并不一定要使用和方法声明中一致的参数名。唯一的要求是保证类型匹配,即你必须保证方法名和参数类型在声明和实现中是一致的。

来看一个例子,下面的代码与之前代码是完全相同的。

这两行代码声明的则和之前的代码中方法不同:

应用内定义的类名称必须唯一,如果你尝试使用一个已存在的类名来创建新类,编译时会出错。

为避免这样的错误,可以添加类名前缀(三个或以上字母)。添加的前缀可以是和你应用相关的,也可以是随意的。之后的所有例子里都使用了类名前缀,如下所示:

历史注记:关于为何许多类前面都带有NS前缀,这是因为Cocoa和Cocoa Touch的历史所致。Cocoa框架最开始用于NeXTStep(NS前缀)系统应用开发。当1996年苹果收购NeXTStep后,NeXTStep系统大部分都被整合进了OSX系统中,包括Cocoa框架和已经存在的类名。而Cocoa Touch框架是为iOS系统使用的,和Cocoa框架类似。一些类在Cocoa和Cocoa Touch中都可用,而另外大部分的类则只适用于自己的平台。

两字母前缀如NS和UI(iOS中用于用户界面元素)被苹果保留。

相比类名,方法名和属性名只需保证在类中唯一既可。虽然C程序中每个函数都必须具有唯一的名字,但OC中却不是这样。OC中必须接受,或者说时常需要在不同的类中建立同名方法。在同一个类声明中,方法名是唯一的,也就是特定方法只能定义一次。但如果你想在子类中覆盖一个父类方法,就必须在子类中使用和父类相同的方法名。

和方法名一样,对象属性和实例变量(参见Most Properties Are Backed by Instance Variables)只需在被定义的类中保证唯一。如果是全局变量,则必须保证在一个应用或工程中名字唯一。

其它的命名规范和建议,请参见Conventions。

当你使用@interface关键字声明一个类 (包括属性和公共方法) 之后,就需要写这些方法的实现。

之前就谈到过,类的声明通常被放在头文件中,扩展名为.h。而类的实现通常被放在源文件中,扩展名为.m。

当在头文件中声明一个类后,你还需要告诉编译器从哪个源文件找到类的实现。OC中提供#import预处理指令来完成这项工作。这条指令可以保证只包含一次头文件,其余则和C语言中#include指令类似。

需要注意,在预处理指令末尾不能添加分号。

类实现的基本语法如下所示:

在类中声明的所有方法都需要在这个文件(.m)中实现。

下面演示一个XYZPerson类,它里面包含一个方法sayHello。

这个类的实现如下所示:

这个例子中NSLog()函数的作用是向终端输出信息。NSLog()函数和C中printf()函数类似,它接受可变数量参数,并且第一个参数必须是OC字符串(以@开头)。

方法的实现写在大括号内({}),和C语言中函数的定义类似。在方法的实现代码中,方法名、参数类型以及返回值类型必须和声明中的完全一致。

OC和C一样是大小写敏感的,请看如下代码:

 

这里的sayhello方法和之前的sayHello方法是完全不同的。

OC中方法名应以小写字母开头,命名原则是:让名称尽可能详细地描述这个方法功能,这与C中函数命名原则不同。当一个方法名中包含多个单词,则可以使用camel case(中间单词的首字母大写,和骆驼驼峰一样起伏,故camel case)来让代码更加易读。

注意灵活使用OC中的代码空白 (增强代码可读性)。你可以选择空格或Tab进行代码缩进,有时还会看到位于空行上的左大括号,如下所示:

如果使用Xcode进行编程,它能按照用户设置自动完成代码缩进。详见Changing the Indent and Tab Width in Xcode Workspace Guide。

在下一章Working with Objects中,你可以找到更多的对象方法实现的例子。

在OC中,类也是一种叫Class的不透明类型对象(即类也有类型,类也是对象)。类没有属性,但它可以接收消息(定义类方法和对象方法有区别)。

类方法的典型应用是factory method(暂译作构造方法),构造方法可以完成对象的空间分配和初始化工作。详见:Objects Are Created Dynamically。(没有对象之前怎么调用方法,需要用到类方法,使用类方法建立对象,再由对象调用对象方法工作:译注)以NSString类为例,它里面有许多的构造方法,有的方法可以建立空字符串对象,有的可以建立包含指定字符串的对象。如下所示:

从上面的例子可以看到,类方法前以加号(+)开始,而对象方法则以减号(-)开头。

类方法的原型声明和对象方法的一样,写在类的声明中。类方法的实现也和对象的一样,写在@implementation块中。

使用Xcode——新建工程——Command Line Tool模板来建立一个OSX工程。提交后,指定工程类别为Foundation。

1.在Xcode中点击新建文件,新建.m和.h文件,并建立XYZPerson类,它继承自NSObject类。

2.在XYZPerson类中加入first name、last name及date of birth属性。

3.声明对象方法sayHello,并对其进行实现。

4.声明一个类的构造方法person。(在学完下一章之前不用实现它。)注意:完成练习后编译代码,你会得到”Incomplete implementation”的提示,这是因为构造方法person还没有进行实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值