您的位置:首页 > 移动开发 > Objective-C

Objective-C 编程语言官网文档(三)-如何定义类

2012-05-30 18:14 549 查看



声明:本文档仅为个人学习过程中顺手翻译之作,方便开发的同胞借鉴参考。如有觉得译的不好不到位的地方,欢迎指正,将及时做出更正

尽量尊重原文档,因为首次Objective-C,有些地方可能直译了没有注意该语言的专有词,希望指正。如需转载,请注明出处



我的编程环境:

IDE:XCODE4.3.1

OS:MACOSX10.7.4

文章来译自:http://developer.apple.com/


如何定义类

大多数面对对象编程都包含为新对象编写代码---定义一个新类。在Objective-C中,类被定义为两部分:

一个接口声明有一些方法以及类的属性,以及它的父类的名字

一个实现类真正的类(包括实现的方法的代码)

下面的几部分通常在它自己的文件里。然而,有是有,一个类的定义可能通过类别的使用跨越几个文件。类目跨越划分类的定义或者扩展一个已经存在的。类别的描述跨越参考“Categories
andExtensions.”


源文件

通常类的接口跟实现是在两个不同的文件中,即便编译器没有强制要求。接口文件必须对任何想要使用该类的人可用。
一个文件可以声明或者实现不止一个类。不过按照惯例,通常每个类都有一个单独的接口文件,以及一个单独的实现文件。保持类接口的独立能够更好的反映它们作为独立实体的状态。
接口和实现文件都用该类命名。实现类文件的名字的扩展名是
.m
,指明它含有Objective-C源码.接口文件可以使用任何别的扩展。因为它是被包含在源文件中的,接口文件通常使用典型的头文件所使用的
.h
扩展名。例如,
Rectangle
类可以定义在
Rectangle.h
以及
Rectangle.m
中。
让一个对象的接口与实现独立开来的做法很好的符合了面向对象设计的思想。一个对象是一种自包含的实体,它可以从外部被当做类似黑盒一样访问。一旦你决定你的对象如何与你程序中的其它元素进行交互,也就是说,一旦你声明好它的接口,你就可以自由的修改它的实现,而不会影响到应用的其它部分了。


类接口

类接口的声明是以编译指令
@interface
开头并以指令
@end
结尾。(所有的Objective-C
编译器指令都是以“@”开头.)

@interfaceClassName:ItsSuperclass

//Methodandpropertydeclarations.

@end

第一行声明标示一个新类名并将其与它的父类联系起来。父类定义了新类在继承树中的位置,就像我们在“Inheritance.”中讨论的那样。
类的方法以及属性定义在之后,类声明结尾处的前面。能被类对象使用的方法名,即类方法,方法前面会有个加号标记;

+alloc;

为类的实例使用的方法,即实例方法,方法前会有一个减号标记。;

-(void)display;

尽管这不是什么惯例,你也可以给类方法和实例方法定义一样的名字。方法也可以跟实例变量名字相同,这更常见,尤其是当方法在变量中返回值时。例如,
Circle
有一个
radius
方法与
radius
实例变量相匹配。
方法的返回值类型的名声使用的是标准的C语法:

-(float)radius;

参数的类型定义也是一样的:

-(void)setRadius:(float)aRadius;

如果一个返回值或者参数类型没有显示的被声明,那么它会假定默认类型为id。早前讲到过的
alloc
方法会返回
id
.
当有更多的参数时,所有的参数会声明在方法名内,在冒号的后面。例如:

-(void)setWidth:(float)widthheight:(float)height;

有较多参数的方法,在声明这些参数时使用逗号以及省略号,就像函数那样:

-makeGroup:group,...;

属性声明的格式是:

@property(attributes)TypepropertyName;

更多关于属性声明的信息可以参考“Declared
Properties.”

注意:过去,接口还要求类的实例变量的声明,数据构造器是每个类实例的一部分。它们定义在
@interface
声明后面的大括号离,方法声明之前。

@interfaceClassName:ItsSuperclass

{

//Instancevariabledeclarations.

}

//Methodandpropertydeclarations.

@end

实例变量代表着实现细节,通常不能被类自身意外访问。并且,你可以在实现块来声明或者使用声明的属性来综合它们。通常你不应该这么做,把实例变量声明在公共接口中,因此你应当忽略这个大括号。


导入接口

接口文件必须被包含在任何依赖这个类接口的源码模块中,即任何需要为这个类创建实例的模块,发送一个消息来调用为这个类声明的方法,或者使用定义在类中的实例变量。接口通常包含在
#import
指令中


#import"Rectangle.h"

这个指令跟
#include
的作用是一样的,唯一的区别是这条指令可以确保同一个文件不会被导入多次。因此在Objective-C的基础文档中的例子大多使用这个指令。
为了反映一个类的定义是建立在所继承的类的定义基础上,那么这个接口文件就以导入父类的接口开始。

#import"ItsSuperclass.h"


@interfaceClassName:ItsSuperclass

//Methodandpropertydeclarations.

@end

这个惯例说明,每个接口文件都间接的包含所有继承的类的接口文件。当一个源文件模块导入一个类接口时,它同时也获得了整个接口树中的接口。
如果有一个precomp—一个预编译头文件—支持父类,你可能会更愿意导入预编译的文件。


引用其它类

当一个接口文件声明了一个类,导入了它的父类,显示包含了所有继承到的类的声明,从
NSObject
开始一直到它的父类。如果接口引用了不在它继承树中的类,它就必须显示的导入它们或者用
@class
指令声明它们


@classRectangle,Circle;

这个指令简单的通知编译器,“Rectangle”跟“Circle”是类名,没有导入它们的接口文件。
在接口文件中,当它静态的给实例变量,返回值以及参数赋予类型时,用到了类名

-(void)setPrimaryColor:(NSColor*)aColor;

引用了
NSColor
类.
因为向这样声明只是简单将类名用作赋予类型,并不依赖任何接口的详情(它的方法和实例变量),
@class
指令已经可以为编译器提供足够的有关编译器能够获得什么信息预警。但是,当类的接口真正使用时(实例创建时,发送消息时),
此时类接口就必须被导入。通常接口文件会使用
@class
指令来生命类,而真正的响应的实现文件会导入它们的接口(因为它需要为这些类创建实例并给它们发消息)。
@class
指令将给编译器以及链接器看的代码量最小化
,同时也是最简单的方式来提供类名的前置声明。让事情变的简单,它可以有效避免导入的文件本身还导入了别的文件时可能出现的潜在问题。例如,当一个类声明了两外一个类的一个静态类型的实例变量,并且他们的两个接口文件相互导入,这种情况下可能两个类都无法正确编译。


接口的职责

接口文件的职责是为其它源代码模块(以及其它程序猿)声明新类。包含它们的类协同工作需要的一些信息(程序猿可能比较喜欢还有点文档).

接口文件告诉用户类是怎么接入继承树的,以及需要用到的其它的类,继承的或者在类中某个位置引用到的。

通过它的方法声明列表,接口文件让其它模块知道什么样的消息可以发送给类对象以及类的实例。每个能在类声明外部使用的方法都声明在接口文件中。实现类内部使用的方法可以忽略


类的实现

T类的定义就像它的声明一样,都是结构化的。以
@implementation
指令开始,
@end
指令结束.
另外,类还可能会在
@implementation
指令后面的大括号中声明实例变量:

@implementationClassName

{

//Instancevariabledeclarations.

}

//Methoddefinitions.

@end

实例变量通常都有声明的属性来指定(可以参考“Declared
Properties”).如果你没有额外的实例变量要声明,那么你可以忽略上面提到的大括号。

@implementationClassName

//Methoddefinitions.

@end

注意:每个实现文件都必须导入它自己的接口。例如
Rectangle.m
类要导入
Rectangle.h
.因为实现不需要重复任何它导入的声明,它可以安全的忽略父类的名字。

类的方法的定义,就像C函数一样,有一堆大括号。在大括号前面,它们的声明跟接口文件中的习惯一样,但没有分号。例如:

+(id)alloc{

...

}

-(BOOL)isFilled{

...

}

-(void)setFilled:(BOOL)flag{

...

}

有多个参数的方法的处理方式就像函数那样就可以了:

#import<stdarg.h>

...

-getGroup:group,...{

va_listap;

va_start(ap,group);

...

}


引用实例变量

默认情况下,实例方法的定义会拥有这个对象所在域的所有实例变量。它可以简单的通过名字来引用它们。尽管编译器创建了完全等价于C的构造器来存储实例变量,但构造器的具体形态被隐藏了。你不需要任何一个构造运算符(.或者->)来引用对象的数据。例如这个方法定义引用了receiver的
filled
实例变量。

-(void)setFilled:(BOOL)flag

{

filled=flag;

...

}

接收对象跟它的
filled
实例变量都没有被声明成这个方法的一个参数,然而这个实例变量还是在它的作用域内。这种方法语法的简化在Objective-C代码书写中是一种有效的速记方式.

当实例变量属于的对象不是receiver的,对象的类型必须通过静态赋予类型显式的呈献给编译器。在引用一个赋予静态类型的对象的实例变量时,将使用构造指针运算符(
->
)。

假设,
Sibling
类声明了一个赋予静态类型的对象,
twin
,作为实例变量:

@interfaceSibling:NSObject

{

Sibling*twin;

intgender;

structfeatures*appearance;

}

只要赋予静态类型的对象的实例变量,在类的有效域内,(就像在这里看到的,twin被赋予了跟类一样的类型),
Sibling
方法就可以直接设置它们:

-makeIdenticalTwin

{

if(!twin){

twin=[[Siblingalloc]init];

twin->gender=gender;

twin->appearance=appearance;

}

returntwin;

}

实例变量的作用域

如果向要对象隐藏它的数据,编译器可以限制实例变量的作用域—即,限制它们在程序中的可见度。但为了提供良好的可伸缩性,它同时允许你显式的把作用域设置为4个等级,每个等级都有对应的编译器指令。

指令
含义
@private

实例变量仅在声明它的类中可被访问。
@protected

实例变量在声明它的类中以及继承它的子类中可被访问。如果一个实例变量没有任何显式的声明指令,

那么默认就是
@protected
作用域.
@public

实例变量随处都可以被访问
@package

使用modern运行时,(modern跟legacyruntime相关介绍可以参考此文),一个
@package
实例变量在可执行的实现类镜像中作用域为
@public
,但对于外部来说则是
@private
.
对于Objective-C
的实例变量,@package
作用域与C
语言中的
private_extern
对其变量和函数的作用类似。任何外部实现类的镜像中的代码试图使用这种实例变量时,会得到一个链接错误。

这种作用域在框架类中的实例变量很有用,在这种地方
@private
可能太过严苛,而
@protected
@public
又授权过度。
图2-1向我们展示了实例变量的四个级别
图2-1实例变量的作用域(
@package
作用域没有展示)


一个作用域指令可以作用到它后面的所有实例变量列表,直到下一个指令出现,或者列表结束。下面的例子中,
age
evaluation
实例变量是私有的;
name
,
job
,
wage
是protected;
boss
是公开的.

@interfaceWorker:NSObject

{

char*name;

@private

intage;

char*evaluation;

@protected

idjob;

floatwage;

@public

idboss;

}

默认情况下,没有标识的实例变量比如上面的
name,
默认为
@protected
.

类声明的所有实例变量,无论它们的标识是什么,都在类定义的作用域之内。例如,一个类声明了一个
job
实例变量,例如上面的
Worker
类,可以在一个方法定义中引用它。

-promoteTo:newPosition

{

idold=job;

job=newPosition;

returnold;

}

显然,如果一个类不能访问它自己的实例变量,那么这个实例变量将毫无用处。

通常,一个类也会访问它继承的实例变量。这种引用实例变量的能力通常与变量以其被继承。类拥有它们作用域内的完整数据结构,这点很有意义,尤其是当你想要一个类的定义像继承来的类一样精巧时。早前提到过的
promoteTo:
方法跟定义在任何类中一样,从
Worker
类中集成到了
job
实例变量。

但是,有时候你可能想要严格限制子类直接获取实例变量:

一旦子类访问了一个实例变量,那么声明这个变量的类就跟它的实现绑定到一起。在以后的版本中,就不能清除这个变量或者改变它的职能而不会无意中破坏了子类。

此外,当子类访问一个继承的实例变量并修改了它的值,它可能在声明这个变量的类中引入一个bug,尤其是在构造器中引用变量时.

要将实例变量作用域限制在声明它的类中,你就应该使用
@private
.子类要想访问父类中
@private
声明的实例变量,就必须通过调用声明在父类中的公开的方法(该方法中提供了访问私有方法的途径),当然前提是这种共有方法存在。

另一种极端是把变量标识为
@public
,让它随处都可被访问。通常,要想获得的存储在实例变量中的信息,其它对象必须给它发消息请求。但公有实例变量可以随处访问。就像C结构中的字段一样。例如

Worker*ceo=[[Workeralloc]init];

ceo->boss=nil;

要注意的是对象必须是被静态赋予类型的。
将实例变量标识为
@public
破坏了对象隐藏自身数据的能力。这与面向对象编程的基本思想背道而驰—使用对象对数据进行封装,可以有效避免查看跟一些无意的错误。应当尽量避免公有实例变量除非确实必要。

self
super
发消息

Objective-C提供了两个元素,可以用在方法定义中来引用执行这个方法的对象—
self
super
.(有点绕,呵呵,看的明白不)

例如,你定义了一个
reposition
方法,它需要在object做任何动作时改变坐标。它可以调用
setOrigin::
方法来改变坐标,你需要做的是发送一个
setOrigin::
给消息给一个对象,这个对象就是
reposition
消息自身的发送对象。当你在写
reposition代码时,你可以通过
self
或者
super

来引用刚提到的对象。

-reposition

{

...

[selfsetOrigin:someX:someY];

...

}

或者:

-reposition

{

...

[supersetOrigin:someX:someY];

...

}

这里的
self
super
都引用的是接收
reposition
消息的对象。然而这两个东东还是有很大区别的.
self
是消息例程传送给每个方法的一个隐藏参数。它是一个本地变量,并可以在一个方法实现中自由的使用。
super
只有作为消息表达式的***时才能替代
self
。作为***,这两个东东的主要区别在于它们是如何影响消息进程的:

self
按照常规搜索方法实现,开始于接收对象的类的派发表中。在上面的例子中,开始于接收reposition消息的对象的类.

super
是一个标识,它告诉编译器去到一个完全不同的地方去查找这个方法实现。它开始于定义了含有
super
的方法的类的父类中。(真心绕啊

).
在上面的例子中,它开始于定义reposition的类的父类中.

只要
super
收到一条消息,编译器就会用另外一个消息例程来替代
objc_msgSend
函数.替代例程直接查找定义类的父类,即发消息给
super
的类的父类
—而不是接收消息的对象的类。


看一个使用self跟super的例子:

当我们使用有继承关系的3个类时,
self
super
的区别将变的很清楚。假如,我们创建一个属于
Low
的对象,
Low
类的父类是
Mid
;
Mid
的父类是
High
.
三个类都定义了一个方法叫做
negotiate
,每个类都处于自己的考虑去使用这个方法。另外,
Mid
类定义了一个NX的方法叫做
makeLastingPeace
,
这个方法用到了
negotiate
方法.上面提到的方法和类可以在图2-2中看到
图2-2High,Mid,andLow的继承关系


假设在
makeLastingPeace
方法实现中(
Mid
类中)使用
self
来只带发送
negotiate
消息的对象:

-makeLastingPeace

{

[selfnegotiate];

...

}

当一条发给了
Low
对象以执行
makeLastingPeace
方法,
makeLastingPeace
会发哦那个一条
negotiate
消息给相同的
Low
对象.
消息例程找到了定义在Low中的
negotiate
版本
,
self
的类
.

但是,如果
makeLastingPeace
的实现使用
super
来作为***,

-makeLastingPeace

{

[supernegotiate];

...

}

消息例程发现了定义在
High
中定义的
negotiate
版本.
而将接收
makeLastingPeace
消息的对象的类(
Low
)
忽略掉并直接跳到父类
Mid
中,因为makeLastingPeace是定义在
Mid
中的.
实现也没有找到
Mid
版本的
negotiate
.

在这个例子中,
super
提供了一种绕过某个方法的途径。这里,我们使用
super
以使
makeLastingPeace
绕过
Mid
中覆写了父类版本的
negotiate
的方法。

无法获取
Mid
版本的
negotiate
方法,可能看起来像个缺陷,但在某些环境下,它是有意为之的:

Low
类的作者故意覆写了
Mid
版本的
negotiate
方法,所以
Low
(以及其子类)
的实例将调用覆写版本的方法。
Low
的设计者并不像
Low
对象执行继承来的方法。

Mid
的方法
makeLastingPeace
方法的作者,在发送
negotiate
消息给
super
(就像在第二个实现中展示的),
故意跳过了
Mid
版本的
negotiate
(以及任何定义在继承了
Mid
类似
Low
的类)以执行定义在
High
类中的版本.第二个
makeLastingPeace
实现的设计者希望使用
High
中定义的
negotiate
版本而不是别的类中的

Mid
版本的
negotiate
仍可以使用,但需要发送一个直接的消息给
Mid
实例来达到这个目的;


使用super

发送给
super
的消息允许方法实现被分发给多个类。你可以覆写一个已经存在的方法来增加或者修改一些东西。并在修改中结合原始方法。

-negotiate

{

...

return[supernegotiate];

}

对于一些任务来说,继承关系中的每一个类都可以实现一个方法,这个方法会做一部分工作并把消息传给
super
完成剩下的.能够初始化新分配的实例
init
方法,就是设计用来像这样工作的。每个
init
方法都有自己初始化定义在类中的实例变量的职责。但在做这些之前,它要发送一个
init
消息给
super
来获取继承来的类并实例化它们的实例变量。每个版本的
init
都遵循这个流程,所以类都是按照继承顺序来初始化它们的实例变量的。

-(id)init

{

self=[superinit];

if(self){

...

}

}

我们也可以关注定义在父类中的某一个方法的核心功能,并让子类把这个方法通过
super
并入这个方法。例如,每个创建实例的类方法都必须为新对象分配存储单元并初始化它类构造器的
isa
变量.
分配通常留下给定义在
NSObject
类中的
alloc
allocWithZone:
方法来做。如果两外一个类覆写了这些方法(一种少见的情况),
它仍然可以通过发送消息给
super
获取基础功能。


重定义self

super
只是一个简单的标识,告诉编译器应该从哪里查找要执行的方法。它仅用于消息的***。但
self
则是一个变量名,使用的方式很多,甚至可以赋予一个新值。
有一种情况就是定义在类方法中。类方法通常并不关心类对象,而是类的实例。例如,许多类方法集成了一个实例的allocation和initialization,通常同时设置实例变量的值。在这种方法中,可能忍不住会发消息给新分配的实例并调用实例
self
,
就像在实例方法中那样。但这样做是错误的。
self
super
都both指向接收的对象—该对象收到一条消息说让他执行这个方法。在一个实例方法内部,
self
指向的是实例。但在类方法的内部,
self
指向的是类对象。我们不能像下例这样做:

+(Rectangle*)rectangleOfColor:(NSColor*)color

{

self=[[Rectanglealloc]init];//BAD

[selfsetColor:color];

returnself;

}

为了避免困惑,在类方法中,通常更好的做法是使用一个变量而不是
self
来指代一个实例:

+(id)rectangleOfColor:(NSColor*)color

{

idnewInstance=[[Rectanglealloc]init];//GOOD

[newInstancesetColor:color];

returnnewInstance;

}

事实上,与其发送
alloc
消息给类方法,不如发送
alloc
self
.
这种方式下,如果类是子类,并且
rectangleOfColor:
消息被子类接收到,这个实例返回的类型会与子类相同(例如,
NSArray
[b]的
[/b]
array
方法被
NSMutableArray
所继承[b])[/b]。

+(id)rectangleOfColor:(NSColor*)color

{

idnewInstance=[[selfalloc]init];//EXCELLENT

[newInstancesetColor:color];

returnnewInstance;

}

可以参考“创建并初始化对象”获取更多实现初始化相关的方法。

英文原文:

http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocDefiningClasses.html#//apple_ref/doc/uid/TP30001163-CH12-SW1)


DefiningaClass

Muchofobject-orientedprogrammingconsistsofwritingthecodefornewobjects—definingnewclasses.InObjective-C,classesaredefinedintwoparts:

Aninterfacethatdeclaresthemethodsandpropertiesoftheclassand
namesitssuperclass

Animplementationthatactuallydefinestheclass(containsthecodethat
implementsitsmethods)

Eachofthesepartsistypicallyinitsownfile.Sometimes,however,aclassdefinitionspansseveralfilesthroughtheuseofafeaturecalledacategory.Categoriescancompartmentalizeaclassdefinitionorextendanexistingone.Categoriesare
describedin“Categories
andExtensions.”


SourceFiles

Althoughthecompilerdoesn’trequireit,classinterfaceandimplementationareusuallyintwodifferentfiles.Theinterfacefilemustbemadeavailabletoanyonewhousestheclass.
Asinglefilecandeclareorimplementmorethanoneclass.Nevertheless,it’scustomarytohaveaseparateinterfacefileforeachclass,ifnotalsoaseparateimplementationfile.Keepingclassinterfaces
separatebetterreflectstheirstatusasindependententities.
Interfaceandimplementationfilestypicallyarenamedaftertheclass.Thenameoftheimplementationfilehas
the
.m
extension,
indicatingthatitcontainsObjective-Csourcecode.Theinterfacefilecanbeassignedanyotherextension.Becauseit’sincludedinothersourcefiles,thenameoftheinterfacefileusuallyhasthe
.h
extension
typicalofheaderfiles.Forexample,the
Rectangle
classwouldbedeclaredin
Rectangle.h
and
definedin
Rectangle.m
.
Separatinganobject’sinterfacefromitsimplementationfitswellwiththedesignofobject-orientedprograms.Anobjectisaself-containedentitythatcanbeviewedfromtheoutsidealmostasablackbox.
Onceyou’vedeterminedhowanobjectinteractswithotherelementsinyourprogram—thatis,onceyou’vedeclareditsinterface—youcanfreelyalteritsimplementationwithoutaffectinganyotherpartoftheapplication.


ClassInterface

Thedeclarationofaclassinterfacebeginswiththecompilerdirective
@interface
and
endswiththedirective
@end
.
(AllObjective-Cdirectivestothecompilerbeginwith“@”.)

@interfaceClassName:ItsSuperclass

//Methodandpropertydeclarations.

@end

Thefirstlineofthedeclarationpresentsthenewclassnameandlinksittoitssuperclass.Thesuperclassdefinesthepositionofthenewclassintheinheritancehierarchy,asdiscussedunder“Inheritance.”
Methodsandpropertiesfortheclassaredeclarednext,beforetheendoftheclassdeclaration.Thenames
ofmethodsthatcanbeusedbyclassobjects,classmethods,areprecededbyaplussign:

+alloc;

Themethodsthatinstancesofaclasscanuse,instancemethods,
aremarkedwithaminussign:

-(void)display;

Althoughit’snotacommonpractice,youcandefineaclassmethodandaninstancemethodwiththesamename.Amethodcanalsohavethesamenameasaninstancevariable,whichismorecommon,especiallyif
themethodreturnsthevalueinthevariable.Forexample,
Circle
hasa
radius
methodthat
couldmatcha
radius
instancevariable.
MethodreturntypesaredeclaredusingthestandardCsyntaxforcastingonetypetoanother:

-(float)radius;

Parametertypesaredeclaredinthesameway:

-(void)setRadius:(float)aRadius;

Ifareturnorparametertypeisn’texplicitlydeclared,it’sassumedtobethedefaulttypeformethodsandmessages—an
id
.
The
alloc
methodillustratedearlierreturns
id
.
Whenthere’smorethanoneparameter,theparametersaredeclaredwithinthemethodnameafterthecolons.Parametersbreakthenameapartinthedeclaration,justasinamessage.Forexample:

-(void)setWidth:(float)widthheight:(float)height;

Methodsthattakeavariablenumber
ofparametersdeclarethemusingacommaandellipsispoints,justasafunctionwould:

-makeGroup:group,...;

Propertydeclarationstaketheform:

@property(attributes)TypepropertyName;

Propertiesarediscussedinmoredetailin“Declared
Properties.”

Note:Historically,theinterfacerequireddeclarationsofaclass’sinstancevariables,
thedatastructuresthatarepartofeachinstanceoftheclass.Theseweredeclaredinbracesafterthe
@interface
declarationandbeforemethoddeclarations:

@interfaceClassName:ItsSuperclass

{

//Instancevariabledeclarations.

}

//Methodandpropertydeclarations.

@end

Instancevariablesrepresentanimplementationdetail,andshouldtypicallynotbeaccessedoutsideoftheclassitself.Moreover,youcandeclarethemintheimplementationblockorsynthesizethemusingdeclaredproperties.Typicallyyoushouldnot,therefore,
declareinstancevariablesinthepublicinterfaceandsoyoushouldomitthebraces.


ImportingtheInterface

Theinterfacefilemustbeincludedinanysourcemodulethatdependsontheclassinterface—thatincludes
anymodulethatcreatesaninstanceoftheclass,sendsamessagetoinvokeamethoddeclaredfortheclass,ormentionsaninstancevariabledeclaredintheclass.Theinterfaceisusuallyincludedwiththe
#import
directive:

#import"Rectangle.h"

Thisdirectiveisidenticalto
#include
,
exceptthatitmakessurethatthesamefileisneverincludedmorethanonce.It’sthereforepreferredandisusedinplaceof
#include
incodeexamplesthroughoutObjective-C–based
documentation.
Toreflectthefactthataclassdefinitionbuildsonthedefinitionsofinheritedclasses,aninterfacefilebegins
byimportingtheinterfaceforitssuperclass:

#import"ItsSuperclass.h"


@interfaceClassName:ItsSuperclass

//Methodandpropertydeclarations.

@end

Thisconventionmeansthateveryinterfacefileincludes,indirectly,theinterfacefilesforallinheritedclasses.Whenasourcemoduleimportsaclassinterface,itgetsinterfacesfortheentireinheritance
hierarchythattheclassisbuiltupon.
Notethatifthereisaprecomp—aprecompiledheader—thatsupportsthesuperclass,youmay
prefertoimporttheprecompinstead.


ReferringtoOtherClasses

Aninterfacefiledeclaresaclassand,byimportingitssuperclass,implicitlycontainsdeclarationsforallinheritedclasses,from
NSObject
on
downthroughitssuperclass.Iftheinterfacementionsclassesnotinthishierarchy,itmustimportthemexplicitlyordeclarethemwiththe
@class
directive:

@classRectangle,Circle;

Thisdirectivesimplyinformsthecompilerthat“Rectangle”and“Circle”areclassnames.Itdoesn’timporttheirinterfacefiles.
Aninterfacefilementionsclassnameswhenitstaticallytypesinstancevariables,returnvalues,andparameters.Forexample,thisdeclaration

-(void)setPrimaryColor:(NSColor*)aColor;

mentionsthe
NSColor
class.
Becausedeclarationslikethissimplyusetheclassnameasatypeanddon’tdependonanydetailsoftheclassinterface(itsmethodsandinstancevariables),the
@class
directive
givesthecompilersufficientforewarningofwhattoexpect.However,whentheinterfacetoaclassisactuallyused(instancescreated,messagessent),theclassinterfacemustbeimported.Typically,aninterfacefileuses
@class
to
declareclasses,andthecorrespondingimplementationfileimportstheirinterfaces(sinceitneedstocreateinstancesofthoseclassesorsendthemmessages).
The
@class
directiveminimizestheamountofcodeseenbythecompilerandlinker,andisthereforethesimplestwaytogive
aforwarddeclarationofaclassname.Beingsimple,itavoidspotentialproblemsthatmaycomewithimportingfilesthatimportstillotherfiles.Forexample,ifoneclassdeclaresastaticallytypedinstance
variableofanotherclass,andtheirtwointerfacefilesimporteachother,neitherclassmaycompilecorrectly.


TheRoleoftheInterface

Thepurposeoftheinterfacefileistodeclarethenewclasstoothersourcemodules(andtootherprogrammers).Itcontainsinformationtheyneedtoworkwiththeclass(programmersmightalsoappreciatea
littledocumentation).

Theinterfacefiletellsusershowtheclassisconnectedintotheinheritancehierarchyandwhatotherclasses—inheritedorsimplyreferredtosomewhereintheclass—areneeded.

Throughitslistofmethoddeclarations,theinterfacefileletsothermodulesknowwhatmessagescanbesenttotheclassobjectandinstancesoftheclass.Everymethodthatcanbeused
outsidetheclassdefinitionisdeclaredintheinterfacefile;methodsthatareinternaltotheclassimplementationcanbeomitted.


ClassImplementation

Thedefinitionofaclassisstructuredverymuchlikeitsdeclaration.Itbeginswithan
@implementation
directive
andendswiththe
@end
directive.
Inaddition,theclassmaydeclareinstancevariablesinbracesafterthe
@implementation
directive:

@implementationClassName

{

//Instancevariabledeclarations.

}

//Methoddefinitions.

@end

Instancevariablesareoftenspecifiedbydeclaredproperties(see“Declared
Properties”).Ifyoudon’tdeclareadditionalinstancevariables,youcanomitthebraces:

@implementationClassName

//Methoddefinitions.

@end

Note:Everyimplementationfilemustimportitsowninterface.Forexample,
Rectangle.m
imports
Rectangle.h
.
Becausetheimplementationdoesn’tneedtorepeatanyofthedeclarationsitimports,itcansafelyomitthenameofthesuperclass.

Methodsfor
aclassaredefined,likeCfunctions,withinapairofbraces.Beforethebraces,they’redeclaredinthesamemannerasintheinterfacefile,butwithoutthesemicolon.Forexample:

+(id)alloc{

...

}


-(BOOL)isFilled{

...

}


-(void)setFilled:(BOOL)flag{

...

}

Methodsthattakeavariablenumberofparametershandlethemjustasafunctionwould:

#import<stdarg.h>


...


-getGroup:group,...{


va_listap;

va_start(ap,group);

...

}


ReferringtoInstanceVariables

Bydefault,thedefinitionofaninstancemethodhasalltheinstancevariablesof
theobjectwithinitsscope.Itcanrefertothemsimplybyname.AlthoughthecompilercreatestheequivalentofCstructurestostoreinstancevariables,theexactnatureofthestructureishidden.Youdon’tneedeitherofthestructureoperators(
.
or
->
)
torefertoanobject’sdata.Forexample,thismethoddefinitionreferstothereceiver’s
filled
instancevariable:

-(void)setFilled:(BOOL)flag

{

filled=flag;

...

}

Neitherthereceivingobjectnorits
filled
instancevariableisdeclaredasaparametertothismethod,yettheinstancevariable
fallswithinitsscope.ThissimplificationofmethodsyntaxisasignificantshorthandinthewritingofObjective-Ccode.
Whentheinstancevariablebelongstoanobjectthat’snotthereceiver,theobject’stypemustbemadeexplicittothecompilerthroughstatictyping.
Inreferringtotheinstancevariableofastaticallytypedobject,thestructurepointeroperator(
->
)isused.
Suppose,forexample,thatthe
Sibling
classdeclaresastaticallytypedobject,
twin
,
asaninstancevariable:

@interfaceSibling:NSObject

{

Sibling*twin;

intgender;

structfeatures*appearance;

}

Aslongastheinstancevariablesofthestaticallytypedobjectarewithinthescopeoftheclass(astheyareherebecause
twin
is
typedtothesameclass),a
Sibling
methodcansetthemdirectly:

-makeIdenticalTwin

{

if(!twin){

twin=[[Siblingalloc]init];

twin->gender=gender;

twin->appearance=appearance;

}

returntwin;

}


TheScopeofInstanceVariables

Toenforcetheabilityofanobjecttohideitsdata,thecompilerlimitsthescopeofinstancevariables—thatis,limitstheirvisibilitywithintheprogram.Buttoprovideflexibility,italsoletsyouexplicitly
setthescopeatfourlevels.Eachlevelismarkedbyacompilerdirective:

Directive
Meaning
@private

Theinstancevariableisaccessibleonlywithintheclassthatdeclaresit.
@protected

Theinstancevariableisaccessiblewithintheclassthatdeclaresitandwithinclassesthatinheritit.Allinstancevariableswithoutanexplicitscopedirectivehave
@protected
scope.
@public

Theinstancevariableisaccessibleeverywhere.
@package

Usingthemodernruntime,an
@package
instancevariablehas
@public
scopeinsidetheexecutable
imagethatimplementstheclass,butactslike
@private
outside.

The
@package
scopeforObjective-Cinstancevariablesisanalogousto
private_extern
for
Cvariablesandfunctions.Anycodeoutsidetheclassimplementation’simagethattriestousetheinstancevariablegetsalinkerror.

Thisscopeismostusefulforinstancevariablesinframeworkclasses,where
@private
maybetoorestrictivebut
@protected
or
@public
too
permissive.
Figure2-1illustratesthelevelsofscoping.
Figure
2-1Thescopeofinstancevariables(
@package
scopenotshown)


Ascopingdirectiveappliestoalltheinstancevariableslistedafterit,uptothenextdirectiveortheendofthelist.Inthefollowingexample,the
age
and
evaluation
instance
variablesareprivate;
name
,
job
,and
wage
are
protected;and
boss
ispublic.

@interfaceWorker:NSObject

{

char*name;

@private

intage;

char*evaluation;

@protected

idjob;

floatwage;

@public

idboss;

}

Bydefault,allunmarkedinstancevariables(like
name
above)are
@protected
.
Allinstancevariablesthataclassdeclares,nomatterhowthey’remarked,arewithinthescopeoftheclassdefinition.Forexample,aclassthatdeclaresa
job
instance
variable,suchasthe
Worker
classshownabove,canrefertoitinamethoddefinition:

-promoteTo:newPosition

{

idold=job;

job=newPosition;

returnold;

}

Obviously,ifaclasscouldn’taccessitsowninstancevariables,theinstancevariableswouldbeofnousewhatsoever.
Normally,aclassalsohasaccesstotheinstancevariablesitinherits.Theabilitytorefertoaninstance
variableisusuallyinheritedalongwiththevariable.Itmakessenseforclassestohavetheirentiredatastructureswithintheirscope,especiallyifyouthinkofaclassdefinitionasmerelyanelaborationoftheclassesitinheritsfrom.The
promoteTo:
method
illustratedearliercouldjustaswellhavebeendefinedinanyclassthatinheritsthe
job
instancevariablefromthe
Worker
class.
However,therearereasonswhyyoumightwanttorestrictinheritingclassesfromdirectlyaccessinganinstancevariable:

Onceasubclassaccessesaninheritedinstancevariable,theclassthatdeclaresthevariableistiedtothatpartofitsimplementation.Inlaterversions,itcan’teliminatethevariable
oraltertheroleitplayswithoutinadvertentlybreakingthesubclass.

Moreover,ifasubclassaccessesaninheritedinstancevariableandaltersitsvalue,itmayinadvertentlyintroducebugsintheclassthatdeclaresthevariable,especiallyifthevariable
isinvolvedinclass-internaldependencies.

Tolimitaninstancevariable’sscopetojusttheclassthatdeclaresit,youmustmarkit
@private
.Instancevariablesmarked
@private
are
onlyavailabletosubclassesbycallingpublicaccessormethods,iftheyexist.
Attheotherextreme,markingavariable
@public
makesitgenerallyavailable,evenoutsideofclassdefinitionsthatinherit
ordeclarethevariable.Normally,togetinformationstoredinaninstancevariable,otherobjectsmustsendamessagerequestingit.However,apublicinstancevariablecanbeaccessedanywhereasifitwereafieldinaCstructure.Forexample:

Worker*ceo=[[Workeralloc]init];

ceo->boss=nil;

Notethattheobjectmustbestaticallytyped.
Markinginstancevariables
@public
defeatstheabilityofanobjecttohideitsdata.Itrunscountertoafundamentalprinciple
ofobject-orientedprogramming—theencapsulationofdatawithinobjectswhereit’sprotectedfromviewandinadvertenterror.Publicinstancevariablesshouldthereforebeavoidedexceptinextraordinarycases.


Messagestoselfandsuper

Objective-Cprovidestwotermsthatcanbeusedwithinamethoddefinitiontorefertotheobjectthatperformsthemethod—
self
and
super
.
Suppose,forexample,thatyoudefinea
reposition
methodthatneedstochangethecoordinatesofwhateverobjectitactson.
Itcaninvokethe
setOrigin::
methodtomakethechange.Allitneedstodoissenda
setOrigin::
message
tothesameobjectthatthe
reposition
messageitselfwassentto.Whenyou’rewritingtherepositioncode,youcanrefertothatobjectaseither
self
or
super
.
The
reposition
methodcouldreadeither:

-reposition

{

...

[selfsetOrigin:someX:someY];

...

}

or:

-reposition

{

...

[supersetOrigin:someX:someY];

...

}

Here,
self
and
super
bothrefertotheobject
receivinga
reposition
message,whateverobjectthatmayhappentobe.Thetwotermsarequitedifferent,however.
self
is
oneofthehiddenparametersthatthemessagingroutinepassestoeverymethod;it’salocalvariablethatcanbeusedfreelywithinamethodimplementation,justasthenamesofinstancevariablescanbe.
super
is
atermthatsubstitutesfor
self
onlyasthereceiverinamessageexpression.Asreceivers,thetwotermsdifferprincipallyinhowtheyaffectthemessagingprocess:

self
searches
forthemethodimplementationintheusualmanner,startinginthedispatchtableofthereceivingobject’sclass.Intheexampleabove,itwouldbeginwiththeclassoftheobjectreceivingtherepositionmessage.

super
is
aflagthattellsthecompilertosearchforthemethodimplementationinaverydifferentplace.Itbeginsinthesuperclassoftheclassthatdefinesthemethodwhere
super
appears.
Intheexampleabove,itwouldbeginwiththesuperclassoftheclasswhererepositionisdefined.

Wherever
super
receivesamessage,thecompilersubstitutesanothermessagingroutineforthe
objc_msgSend
function.
Thesubstituteroutinelooksdirectlytothesuperclassofthedefiningclass—thatis,tothesuperclassoftheclasssendingthemessageto
super
—ratherthantotheclass
oftheobjectreceivingthemessage.


AnExample:Usingselfandsuper

Thedifferencebetween
self
and
super
becomes
clearwhenusingahierarchyofthreeclasses.Suppose,forexample,thatwecreateanobjectbelongingtoaclasscalled
Low
.Thesuperclassof
Low
is
Mid
;
thesuperclassof
Mid
is
High
.Allthreeclassesdefineamethodcalled
negotiate
,
whicheachclassusesforitsownpurpose.Inaddition,
Mid
definesanambitiousmethodcalled
makeLastingPeace
,
whichitselfemploysthe
negotiate
method.TheclassesandthosemethodsareillustratedinFigure
2-2.
Figure
2-2ThehierarchyofHigh,Mid,andLow


Supposethattheimplementationof
makeLastingPeace
(inthe
Mid
class)
uses
self
toindicatetheobjecttosendthe
negotiate
messageto:

-makeLastingPeace

{

[selfnegotiate];

...

}

Whenamessageissenttoa
Low
objecttoperformthe
makeLastingPeace
method,
makeLastingPeace
sends
a
negotiate
messagetothesame
Low
object.Themessagingroutinefindstheversionof
negotiate
defined
in
Low
,theclassof
self
.
However,iftheimplementationof
makeLastingPeace
insteaduses
super
as
thereceiver,

-makeLastingPeace

{

[supernegotiate];

...

}

themessagingroutinefindstheversionof
negotiate
definedin
High
.
Itignorestheclass(
Low
)oftheobjectthatreceivedthe
makeLastingPeace
messageand
skipstothesuperclassof
Mid
,because
Mid
iswhere
makeLastingPeace
is
defined.Neitherimplementationfindsthe
Mid
versionof
negotiate
.
Asthisexampleillustrates,
super
providesawaytobypassamethodthatoverridesanothermethod.Here,theuseof
super
enabled
makeLastingPeace
to
bypassthe
Mid
versionof
negotiate
thatredefinedthe
High
version
ofthatmethod.
Notbeingabletoreachthe
Mid
versionof
negotiate
,
asjustdescribed,mayseemlikeaflaw,butunderthecircumstancesit’sintentional:

Theauthorofthe
Low
classintentionallyoverrodethe
Mid
version
of
negotiate
sothatinstancesof
Low
(anditssubclasses)wouldinvoketheredefinedversion
ofthemethodinstead.Thedesignerof
Low
didn’twant
Low
objectstoperformtheinherited
method.

Theauthorofthe
Mid
method
makeLastingPeace
,
insendingthe
negotiate
messageto
super
(asshowninthesecondimplementation),intentionally
skippedoverthe
Mid
versionof
negotiate
(andoveranyversionsthatmightbedefinedin
classeslike
Low
thatinheritfrom
Mid
)toperformtheversiondefinedinthe
High
class.
Thedesignerofthesecondimplementationof
makeLastingPeace
wantedtousethe
High
version
of
negotiate
andnoother.

The
Mid
versionof
negotiate
couldstill
beused,butitwouldtakeadirectmessagetoa
Mid
instancetodoso.


Usingsuper

Messagesto
super
allow
methodimplementationstobedistributedovermorethanoneclass.Youcanoverrideanexistingmethodtomodifyoraddtoitandstillincorporatetheoriginalmethodinthemodification:

-negotiate

{

...

return[supernegotiate];

}

Forsometasks,eachclassintheinheritancehierarchycanimplementamethodthatdoespartofthejobandpassesthemessageonto
super
for
therest.The
init
method,whichinitializesanewlyallocatedinstance,isdesignedtoworklikethis.Each
init
method
hasresponsibilityforinitializingtheinstancevariables
definedinitsclass.Butbeforedoingso,itsendsan
init
messageto
super
tohavethe
classesitinheritsfrominitializetheirinstancevariables.Eachversionof
init
followsthisprocedure,soclassesinitializetheirinstancevariablesintheorderofinheritance:

-(id)init

{

self=[superinit];

if(self){

...

}

}

It’salsopossibletoconcentratecorefunctionalityinonemethoddefinedinasuperclassandhavesubclassesincorporatethemethodthroughmessagesto
super
.
Forexample,everyclassmethodthatcreatesaninstancemustallocatestorageforthenewobjectandinitializeits
isa
variabletotheclassstructure.Allocationistypically
lefttothe
alloc
and
allocWithZone:
methodsdefinedinthe
NSObject
class.
Ifanotherclassoverridesthesemethods(ararecase),itcanstillgetthebasicfunctionalitybysendingamessageto
super
.


Redefiningself

super
issimplyaflagtothecompilertellingitwheretobeginsearchingforthemethodtoperform;it’susedonlyasthe
receiverofamessage.But
self
isavariablenamethatcanbeusedin
anynumberofways,evenassignedanewvalue.
There’satendencytodojustthatindefinitionsofclassmethods.Classmethodsareoftenconcernednotwiththeclassobject,butwithinstancesoftheclass.Forexample,manyclassmethodscombineallocation
andinitializationofaninstance,oftensettingupinstancevariablevaluesatthesametime.Insuchamethod,itmightbetemptingtosendmessagestothenewlyallocatedinstanceandtocalltheinstance
self
,
justasinaninstancemethod.Butthatwouldbeanerror.
self
and
super
bothreferto
thereceivingobject—theobjectthatgetsamessagetellingittoperformthemethod.Insideaninstancemethod,
self
referstotheinstance;butinsideaclassmethod,
self
refers
totheclassobject.Thisisanexampleofwhatnottodo:

+(Rectangle*)rectangleOfColor:(NSColor*)color

{

self=[[Rectanglealloc]init];//BAD

[selfsetColor:color];

returnself;

}

Toavoidconfusion,it’susuallybettertouseavariableotherthan
self
torefertoaninstanceinsideaclassmethod:

+(id)rectangleOfColor:(NSColor*)color

{

idnewInstance=[[Rectanglealloc]init];//GOOD

[newInstancesetColor:color];

returnnewInstance;

}

Infact,ratherthansendingthe
alloc
messagetotheclassinaclassmethod,it’softenbettertosend
alloc
to
self
.
Thisway,iftheclassissubclassed,andthe
rectangleOfColor:
messageisreceivedbyasubclass,theinstancereturnedisthesametypeasthesubclass(forexample,the
array
method
of
NSArray
isinheritedby
NSMutableArray
).

+(id)rectangleOfColor:(NSColor*)color

{

idnewInstance=[[selfalloc]init];//EXCELLENT

[newInstancesetColor:color];

returnnewInstance;

}

See“CreatingandInitializingObjects”formoreinformationaboutimplementinginitializer
andrelatedmethods.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: