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

Objective-C 编程语言官网文档(二)-对象,类以及消息

2012-05-29 00:22 507 查看



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

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

我的编程环境:

IDE:XCODE4.3.1

OS:MACOSX10.7.4

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


Objects,Classes,andMessaging

本章介绍了Objective-C中使用,实现的对象,类以及消息的相关基础知识。除此之外还介绍了一下运行时环境的相关内容。

运行时系统

Objective-C语言在编译时、链接时直到运行时都是尽量的推迟做决定。只要可能它都是动态的执行诸如创建对象和调用方法的操作。因此,语言不仅需要一个编译器,同时还需要一个运行时系统来执行编译了的代码。对于Objective-C语言来说,运行时系统扮演着类似操作系统的角色。通常你并不需要直接与运行时环境进行交互。要理解运行时提供的功能的话,可以参考Objective-C
RuntimeProgrammingGuide.

对象
面向对象编程是围绕对象构建的。一个对象相关的数据,用某些操作运算便能影响该数据,Objective-C提供了一种数据类型,用以识别一个对象变量,即便没有指定该对象的类。

对象基础

面向对象编程是围绕对象构建的。一个对象相关的数据,某些操作运算便能影响该数据。在Objective-C中,这些运算被称作对象的方法。受方法影响的数据是它的实例变量。(在别的环境中,它们可能是指实例变量或者成员变量)。本质上来讲,一个对象将数据结构(实例变量)以及一组进程(方法)绑定到了自身的编程单元中。

Objective-C中,一个对象的实例变量是本质体现,通常,你只能通过对象的方法来访问该对象的状态(你可以指定是否子类或者其它的对象能够通过使用域指令直接访问成员变量)。对于其它想要得到有关对象的信息的,应该提供一个方法来提供上述信息。例如,一个矩形应该有方法可以获取它的尺寸以及位置。

要说更多的话,一个对象仅看得到我们为它设计的方法。而不能错误滴执行了为别的对象设计的方法。就像C方法避免它的本地变量一样,将它们对其它程序不可见,
一个对象将隐藏它的实例变量以及它的方法实现。


id

Objective-C中,对象标示符是一种另类的数据类型:id。这种类型一种可以用于所有对象的泛型类型,忽略具体的类,并且可以用于一个类的实例以及类对象自身。

idanObject;

对于Objective-C的面向对象构造,方法返回值,id替代int作为默认数据类型。(对于严格的C来说,函数返回值,int仍然是默认类型)

关键字nil用于定义一个空对象,一个值为0的id,nil,可以在objc/objc.h中找到其它类型的Objective-C基础类型定义。

下面的例子中id被定义为指向一个数据构造函数的指针。

typedefstructobjc_object{

Classisa;

}*id;

每个下面这种类型对象都有一个isa变量,该变量可以告诉我们它是哪个类的实例。因为Class类型自身被定义成了一个指针。

typedefstructobjc_class*Class;

isa
通常被成为“
isa
指针.”

动态类型

id类型是完全不严格的。就它自身来说,它没有产生任何有关一个对象的信息,当然除了它是一个对象。从某种意义上来说,一个程序通常需要得到更多有关一个对象它所包含的特定信息。因为id类型标示符无法为编译器提供这种特定信息,这些对象必须在运行时能够提供出这些信息。

isa实例变量指明了对象的类,该对象是什么类型的对象。拥有相同的行为(方法)以及相同的数据(实例变量)的对象是属于相同的类。

这种对象是运行时的动态类型。只要需要,运行时系统随时都可以找到该对象所属的准确的类,只要通过询问该对象。(要想了解关于运行时的信息,可以看考Objective-C
RuntimeProgrammingGuide一文)。Objective-C中的动态类型是作为动态绑定的基础进行服务的,晚些时候我们会进一步进行讨论。

isa变量同样可以让对象进行自省操作--以找到关于它们自己的信息(或者其它对象)。编译器会记录供运行时系统使用的有关类在数据结构中的定义相关信息。使用运行时系统时,你可以决定是否一个对象实现了一个特定的方法或者查找它的父类的名字。

我们将在“Classes”一章详细讨论对象类。

它还可以通过静态的在源代码中输入类名来为编译器提供对象类的信息。类是特定类型的对象,并且类名是作为类型名来使用的。可以参考“Class
Types”和“Enabling
StaticBehavior.”

内存管理

在任何程序中,当对象不在使用时确保它们被正确释放都是极其重要的,否则你的应用的内存轨迹会超出它们实际需要的量。同样重要的是,我们应当确保不要去释放正在使用中的对象。

Objective-C提供了三种方式来让我们实现上述目标:

AutomaticReferenceCounting(ARC),自动引用统计,编译器会推断出所有对象的生命周期

ManualReferenceCounting(MRC),手动引用统计,有时也叫MRR,即手动保持,释放。用于你完全掌控并决定对象的生命周期。

详情可以参考AdvancedMemoryManagementProgramming
Guide.

Garbagecollection,垃圾回收,将决定对象生命周期的责任传递给自动回收器

可以参考GarbageCollectionProgrammingGuide.
(但文档不适用于iOS—IOS的你可以通过访问IOS开发中心获取该文档.

对象消息

本节主要介绍了发送消息的语法,包括如何嵌套消息异常。我们还将讨论有关一个对象实例变量的可见域方面的问题。以及多态和动态绑定的概念。


发消息的语法

要让一个对象去做一些事情,你需要给它他送一个消息,告诉它去请求一个方法。在Objective-C中,消息异常被放在方括号中

[receivermessage]

receiver是一个对象,并且消息告诉它该去做什么。在源代码中,小时仅是一个方法的名字以及需要传递给它的参数。当消息发送后,运行时系统会从receiver的指令系统中选择恰当的方法并调用它。

例如,下面这条消息告诉
myRectangle
对象执行它的
display
方法,这个方法会让这个矩形显示它自己;

[myRectangledisplay];

消息后面会有一个分号“
;
”就像任何C中的语句一样。

因为消息中的方法名用于选择实现方法,在消息中的方法名通常也被称为选择器(selectors)。

方法可以带参数。单个参数的消息,参数后面会有一个冒号(
:
),冒号后面是参数的值

[myRectanglesetWidth:20.0];

对于有多个参数的方法,形式类似单个参数,多个参数间用空格分开,格式也是参数名:参数值

[myRectanglesetOriginX:30.0y:50.0];//Thisisagoodexampleof

//multipleparameters

选择器的名字包含名字的所有部分,包括冒号,如上面看到的例子那样,当然还可以包含任何其它需要的,比如返回值类型以及参数类型



注意
Objective-C选择器名的字部分不是可选的,它们的顺序也不能改变。在一些其它的语言中,“namedparameters”以及“keywordparameters”在运行时可以改变,可以拥有默认值,可以有不同的顺序,并且可能有额外的命名参数。这些特性Objective-C是没有的.
无论怎么说,一个Objective-C方法声明只是一个简单的带有两个额外参数的C函数(可以参考Objective-C
RuntimeProgrammingGuide文档中的“Messaging”获取更多信息).
因此,Objective-C方法声明的结构不同于那些使用named或者keyword参数的语言,比如Python,就像下面的Python例子所描述的:

deffunc(a,b,NeatMode=SuperNeat,Thing=DefaultThing):

pass

在这个Python例子中,
Thing
NeatMode
可能在被调用时被忽略或者有不同的值

在日常操作中,
Rectangle
类可以简单的替代实现
setOrigin::
方法,而没有第二个参数的任何标签,调用的时候如下面的例子:

[myRectanglesetOrigin:30.0:50.0];//Thisisabadexampleofmultipleparameters

语法上是合法的,
setOrigin::
没有将方法名与参数交叉。因此第二个参数没有任何标签,但这样会让看这段代码的人很困惑,不知道这些参数是做什么的。

一个方法有可变参数也是可能的,尽管这种情况很少出现。额外的参数用逗号分开。在下面的例子中
makeGroup:
方法传递了一个必须的参数(
group
)一起另外三个可选的参数:

[receivermakeGroup:group,memberOne,memberTwo,memberThree];

类似标准的C函数,方法可以有返回值。

BOOLisFilled;

isFilled=[myRectangleisFilled];

我们注意到变量和方法可以拥有一样的名字。

一个消息表达式可以嵌套在另外一个消息的内部。下面的例子是一个矩形的颜色设置给了另外一个矩形的颜色。

[myRectanglesetPrimaryColor:[otherRectprimaryColor]];

Objective-C同样也提供了逗点(
.
)操作符,它可以提供方便的途径来调用一个对象的可访问方法。逗点操作符通常用在与声明的属性连接的特性,具体可以参考(“Declared
Properties”),逗点操作符的语法描述可以看“Dot
Syntax.”

发送消息给nil

Objective-C中,发送消息给nil是有效的—只是在运行时不会产生什么效果。在Cocoa中可以有几个方面来利用这种特性。从发送到nil的消息返回的值同样也是有效的:

如果方法返回一个对象,那么一条发往
nil
的消息将返回
0
(
nil
).
例如:

Person*motherInLaw=[[aPersonspouse]mother];

如果这里的
spouse
对象是
nil
,
那么
mother
会发送到
nil
并且方法会返回
nil
.

如果方法返回任何指针类型,任何整型的表两只大小会小雨或者等于
sizeof(void*)
,一个
float
,
double
,
longdouble
,或者
long
long
,那么一条消息发送到
nil
将返回
0
.

如果方法返回一个
struct
,就像Mac
OSXABIFunctionCallGuide定义的那样,从寄存器中返回,那么一条消息发往
nil
将对于每个
struct
中的成员返回
0.0
,其它
struct
数据类型不会被填充没0

如果方法返回任何前述的值类型,那么发往
nil
消息的返回值将是undefined.

Thefollowingcodefragmentillustratesavaliduseofsendingamessageto
nil
.

idanObjectMaybeNil=nil;


//thisisvalid

if([anObjectMaybeNilmethodThatReturnsADouble]==0.0)

{

//implementationcontinues...

}


Receiver的实例变量

一个方法可以自动访问获取到的对象的实例变量。你不必把它们当做参数传给方法。例如上面提到过的
primaryColor
方法没有带任何参数,它还是为
otherRect
找到了主颜色并将其返回.
每个方法都采用receiver以及它的实例变量,无需将它们声明为参数。
这个约定简化了Objective-C代码.它也支持面向对象编程的程序员所想的对象及消息。消息传给receivers就好像信件邮寄到你的家里。消息从外部携带着参数给receiver;它们没必要再将receiver传给它自己.
一个方法只可以自动访问获取到receiver的实例变量。如果它要求其它有关存储在另一个对象的变量信息,那么它必须发送一条消息给哪个对象,向它请求以获取该变量的内容。上面提到的
primaryColor
isFilled
方法就是处于这个目的使用的。
需要获取更多关于实例变量的信息,可以参考“Defining
aClass”


多态

像上面一些例子描述的,在Objective-C中的消息展现出来的功能就像标准的函数调用。但是,因为方法是“属于”一个对象,消息并不需要像一个传统的函数调用那样行事。
通常,一个对象可以操作我们为其设计好的方法。为其它对象设计的方法不能迷惑它们,即便方法名相同。但是,两个对象可以对同一个消息做出不同的应答。例如,每个对象在接收到
display
消息时都可以以其独有的方式展现自身。
这个特性,被称为多态,在面向对象编程中扮演着很重要的角色。同样重要的还有动态绑定,它使得我们写的代码可以应用到许许多多不同的对象中,无需在你写这些代码时就选择对象的类型是什么。还可能是应用到晚些时候才开发的对象上,被其它程序猿用在别的项目中。如果你写的代码发送了一个
display
消息给
id
变量,任何拥有
display
方法的对象都称为潜在的接收者.


动态绑定


在函数调用跟消息间有个很显著的区别,就是函数以及它的阐述在编译好的代码中是结合在一起的。但消息以及它的接收对象并不结合在一起,知道代码开始运行并且消息被发送。因此,具体的用于回应消息的方法调用可以在运行时决定,而非在编译时及决定好。

当消息发送后,一个运行时消息例程会查看***以及消息中的方法名。它会定位匹配***的方法实现并“调用”这个方法,给它传递一个指向***实例变量的指针。(要了解更多关于这个例程的信息,可以参考Objective-C
RuntimeProgrammingGuide中的“Messaging”一节)
消息的动态绑定方法与多态特性赋予了面向对象编程强大的可伸缩性。因为每个对象都有它们自己的方法,一条Objective-C语句可以得到各种结果,并非通过改变消息,而是靠改变接收消息的对象。***可以在程序运行时动态决定。***可以根据用户的动作等做出决定。
当执行基于ApplicationKit(AppKit)的代码时,例如,用户决定哪个对象需要从菜单命令中接收消息,如剪切,复制以及粘贴等。消息会找到当前正控制当前选项的对象。一个用来显示文本的对象与一个用来显示扫描图像的对象会对复制这个消息产生不同的反应。因为消息直到运行时才会选择所需的方法(换句话来讲,因为消息绑定的方法只有运行时才会执行),
这些行为上的不同让不同方法区别开来。发送消息的代码不用关注它们。它甚至不必猜测可能性。一个应用的对象都能以其队友的方式对复制这个消息做出回应。
Objective-C让动态绑定更进的一步,并且甚至允许被发送给变量的消息运行时才决定。至于如何实现,我们将在Objective-C
RuntimeProgrammingGuide中的“Messaging”一节进行讨论。

动态方法方案
你可以在运行时使用动态方法放来提供类和实例方法的实现。具体可以参考Objective-C
RuntimeProgrammingGuide的“Dynamic
MethodResolution”一节。


逗点语法

Objective-C提供了逗点(
.
)操作符,它可以为我们提供另外一种具有传统的方括号([])的功能的方式来访问方法。用法与C中类似

myInstance.value=10;

printf("myInstancevalue:%d",myInstance.value);

下面的例子与上例等效

[myInstancesetValue:10];

printf("myInstancevalue:%d",[myInstancevalue]);

因此,如果你要通过使用访问方法来访问一个对象自己实例变量,你需要显示的调用self例如:

self.age=10;

等价于

[selfsetAge:10];

如果在属性传递中产生了一个
nil
值,那么结果跟把相同的消息发送给nil是一样的。下列几对语句效果是等效的:

//Eachmemberofthepathisanobject.

x=person.address.street.name;

x=[[[personaddress]street]name];


//ThepathcontainsaCstruct.

//Thiswillcrashifwindowisnilor-contentViewreturnsnil.

y=window.contentView.bounds.origin.y;

y=[[windowcontentView]bounds].origin.y;


//Anexampleofusingasetter.

person.address.street.name=@"OxfordRoad";

[[[personaddress]street]setName:@"OxfordRoad"];


面向对象编程通常都是围绕着一系列对象进行构建。一个基于Cocoa框架的程序也许会用到对象:
NSMatrix
,
NSWindow
,
NSDictionary
,
NSFont
,
NSText
,
以及更多其它的对象。编程通常会用到不止一个类似的对象或者类,比如几个
NSArray
对象或者
NSWindow
对象。

在Objective-C中,你通过定义类来定义一组对象。类的定义是对一组某种对象的协议。它声明的实例变量称为所有类变量的一部分,并且它定义了一组所有类中变量都能使用的方法。

编译器仅诶每个类创建了一个可以访问的对象,一个类对象,这个类对象知道如何创建属于该类的新对象。(正因为这个,它通常被称为工厂对象)。这个类对象是类编译后的版本。由它创建的对象都是该类的实例。运行时干着你程序大多数活的对象都是类对象的实例。

所有类的实例都有相同的一套方法,并拥有从同一个模子得到的一套实例变量。每个对象获取到的是它们自己的实例变量,但方法是共享的。

按照惯例,类名都是以大写字母开头,(例如
Rectangle
);实例名则通常以小写字母开头(例如
myRectangle
).


继承

类的定义都是累加性的;每个你定义的新类都是基于两外一个类,并从那里继承方法与实例变量。新的类简单的添加或者修改继承的东西。继承来的东东则不必再重复写。

Inheritance继承将所有类联系在一个继承树中,顶端是一个类。当基于基础框架写代码时,根类通常都是
NSObject.
每个类(根类除外)都有父类,比离根类更近,并且所有类(包括根类)都可以成为任意多个子类的父类,当然跟根类更远。图
1-1描述了用于绘图的几个类的继承关系
Figure1-1Somedrawingprogramclasses


图1-1显示
Square
类是
Rectangle
类的一个子类,
Rectangle
类是
Shape
类的一个子类,
Shape
又是
Graphic
类的一个子类,
Graphic
NSObject
的子类。继承是累加的。所以一个
Square
对象拥有
Rectangle
,
Shape
,
Graphic
,
以及
NSObject
的方法跟实例变量。所以,我们可以说一个
Square
对象不仅是一个正方形,同样也是一个矩形,形状,图形以及
NSObject
的对象


除了
NSObject
以外的每个类都可以视为另外一个类的属性或者适配器。每个继承的子类进一步修改累积继承的总量。
Square
类仅定义了仅够将一个举行转化为一个形状所需的东西。

当你在定义一个类时,你需要通过声明它的父类把它关联到继承树中;每个你创建的类必须是其它某个类的子类,(除非你定义了一个新的根类).大量潜在的子类是可能的。Cocoa包含
NSObject
类,许多框架包含了多余250个扩展类。有些类可以用于跟你的程序交互。其它的你可以希望按照你的实际需要创建该类的适配器子类。

一些框架类定义了几乎你要用到的一切,但预留了一些特性让子类来实现。你就可以写几行代码就可以重用其它程序猿完成的框架代码了。

NSObject类

NSObjec
t
是一个根类,没有父类。它为Objective-C
对象以及对象的交互定义了基本框架.它赋予了那些继承自它的类以及类的实例能够作为对象一样运行并与运行时系统进行合作互动的能力。
如果一个类不想从另外一个类继承任何特定的动作,它任然还是
NSObject
类的子类.该类的实例必须至少拥有运行时像一个Objective-C对象的能力。继承自
NSObject
类的这种能力简单但比从其它任何新类中改造的定义更加可靠。

注意:实现一个新的根类是一项让人头疼的任务,并且可能有许多致命的问题。这个类要重复许多
NSObject
类现在所做的事情,例如分配实例,将它们与它们的类连接,并在运行时系统中识别它们。因此,你应当使用Cocoa提供的
NSObject
类来作为你的根类.
更多信息可以参考NSObject
ClassReference以及NSObject
ProtocolReference.

继承实例变量

当一个类对象创建了一个新的实例,这个新的对象将不仅包含它所属类的实例变量,而且还包括为它的上层父类,上上层父类,上上上层。。。直到根类.因此定义在NSObject的
isa
实例变量将称为所有对象的一部分。
isa
将每个变量与它们的类联系在一起。
图1-2向我们战士了几个实例变量,它们可以定义在一个特定的
Rectangle
类的实现中。要注意的是,这些让一个对象成为矩形的变量们被加到能让其成为一个形状的对象中,而能使其成为形状的变量们又被加到了能让其成为图形的变量中。。。依此类推。
Figure1-2Rectangleinstancevariables


并不是每一个类都需要声明实例变量。它可以简单的定义一些新方法并在需要实例变量时使用它继承的类实例变量。例如,
Square
肯能从来都没有过它自己的实例变量

继承方法

一个对象不仅可以访问它自身类的方法,还可以访问父类的方法,以及。。。直到继承树的根类中的方法.比如,一个
Square
对象可以使用来自以下类中的方法:
Rectangle
,
Shape
,
Graphic
,
以及
NSObject
也可以使用它自己类中的方法。
任何一个你程序中新定义的类都可以利用写在继承树中的所有父类中的代码。这种类型的继承是面向对象编程的主要好处之一。当你使用Cocoa提供的面向对象框架时,你的程序就可以使用这些框架类提供的基础功能。你只需为你的应用对基础实现的个性化定制代码到你的应用中就O了。
类对象同样继承自继承树中它们的父类。但因为它们并没有实例变量(只有实例才有),它们只继承方法。

方法重写

继承有一个有用的例外:当你定义一个新类的时候,你可以实现一个新的方法,与继承树某个父类中定义的方法名相同。新方法会覆盖老方法。这个新类的实例会执行新的方法,而非父类中的同名方法,这个新类的子类也会继承这个新的方法而非老的方法。
例如:
Graphic
定义了一个
display
方法,而
Rectangle
重写并定义了它自己版本的
display
方法。
Graphic
的方法对所有继承自
Graphic
类的对象都是可用的,但
Rectangle
的对象则不同,
它将执行自己版本的
display
方法。
尽管重写一个方法会阻断原始版本的继承,但定义在新类的中的其它方法可以跳过被重新定义的方法,并找到原始的方法。(具体方法可以参考“Messages
toselfandsuper”).
一个被重定义的方法也可以与被覆写的方法结合使用。
尽管子类可以重写它继承的方法但无法重写继承的实例变量。因为对象拥有继承时分配给每个实例变量的内存信息,你不能通过声明一个拥有同样名字的实例变量来重写继承来的变量。如果你真这么干了,编译器就会冲你使鬼脸。


抽象类

一些类被设计仅仅或主要用来让其它类继承它们。这些抽象类的方法和实例变量可以被许多的子类来使用。抽象类通常不会完全实现自己的方法,但包含一些很有用的代码,可以减少实现它们的子类的障碍与麻烦。(因为抽象类需要有子类才有用,所以它们有时也被称为抽象父类)

不像其它一些语言,Objective-C没有专门的语法来把一个类标示为一个抽象类,也没有阻止你去创建一个抽象类的实例。
NSObject
类就是Cocoa中一个抽象类的典型例子。
你绝对不要在一个应用中使用
NSObject
类的实例—因为这可不是一个好主意;因为它只是一个对象,撒也干不了,唯一的用处是哪天让你自己困惑,告诉自己,“噢,该死,这是哪个蠢货在这里为一个抽象类创建了一个实例,难道是自己么”


NSView
类,提供了另外一个抽象类的例子,但你可能偶尔会用到它的实例。
抽象类通常包含用来定义一个应用结构的代码。当你为这些类创建子类时,你的新类的实例就可以很好的符合应用结构的要求并与其它对象以其有爱的以其工作。


类的类别

一个类的定义是为了一个特定类型的对象的特性的集合。类实际上定义了数据类型,类型是不仅仅是基于类定义(实例变量)的数据类型,同样包括行为的定义。(方法).
只要是C中允许的形式,类名都可以出现在代码中。—例如,作为
sizeof
操作符的参数:

inti=sizeof(Rectangle);

静态类型

你可以在id的位置使用一个类名来指定对象的类型:

Rectangle*myRectangle;

因为这种声明一个对象的方法告知了编译器有关这个对象类型的信息,它被称作静态类型。因为id实际上是个指针,对象被静态的作为指针赋予了一个类。对象总是通过指针类指定类型。静态类型使指针显示出来;id将它隐藏了。
静态类型允许编译器做一些类型检查—例如,当一个对象收到一个看起来它不能相应的消息时给予警告。—并且放宽对id类型对象使用的严格限制.另外,对其它想要阅读你代码的人来说,它可以让你的意图更加清晰。但是,它并不能替代或者战胜动态绑定特性在运行时动态决定***的类的优点。
一个对象可以为它自己的类或者继承来的父类静态的赋予类型,例如,因为继承使
Rectangle
对象同时也是一个
Graphic
对象
(就像图Figure
1-1中展示的那样),一个
Rectangle
实例也可以静态的赋予
Graphic
类型:

Graphic*myRectangle;

为父类静态的赋予类型在这里可以行得通是因为
Rectangle
对象同样也是
Graphic
对象.
远不止这些,因为它还有
Shape
Rectangle
对象的实例变量以及方法能力,但一个
Graphic
对象就不行.
处于类型检查的目的,编译器认为
myRectangle
的类型是
Graphic
.但运行时,如果运行时,
myRectangle
被分配并初始化为
Rectangle
的一个实例,它们会被让成相同的一个对待。
静态赋予类型以及它的好处可以参考“Enabling
StaticBehavior”

类型自省

实例可以在运行时获取到它们的类型。定义在
NSObject
类中的
isMemberOfClass
方法,
会检查***是否是某个特定类的实例。

if([anObjectisMemberOfClass:someClass])

...

同样定义在
NSObject
类中的
isKindOfClass:
方法,
则会检查***是否继承自某个类或者是否是某个类的一员(是否在继承树路径中):

if([anObjectisKindOfClass:someClass])

...

对于那些类在方法isKindOfClass返回YES的类,那么这些***可以被静态的赋予类型。
自省不仅局限于类型信息。再后面的小节中会讨论返回类对象的方法,报告是否一个对象能对一个消息进行响应,并获取其它信息。
有关方法
isKindOfClass:
,
isMemberOfClass:
,以及相关方法的更多信息NSObject
ClassReference


类对象

一个类的定义包含很多种信息,大多数是关于类的实例的:

类名以及它的父类

用于描述一组实例变量的模板

方法名,返回类型,以及参数类型的声明

方法实现

这些信息将被编译并记录在数据结构中,并在运行时可用。编译器只会创建一个对象----一个类对象,代表这儿类。类对象可用访问有关这个类的所有信息,也就是说有关这个类的实例的是什么样的这类主要信息。类对象可用根据类中的定义产生新的实例。
尽管一个类对象持有一个类实例的协议,但它自身并不是一个实例。它自身也没有实例变量,但是,在类的定义可用包含为类对象使用的方法,类方法,有别于实例方法。一个类对象会从继承树中继承树中父类中的类方法。就像实例会继承实例方法一样。
在源代码中,类名代表着类对象。在下面的例子中
Rectangle
类返回了该类的版本号,使用的是从
NSObject
类中继承的方法:

intversionNumber=[Rectangleversion];

但是,类名代表的类对象仅作为消息表达式里的***。其它地方,你需要请求实例或者类返回类
id
.两者都返回
class
消息:

idaClass=[anObjectclass];

idrectClass=[Rectangleclass];

像这些例子展示的,类对象可用像其它对象那样,赋予id类型。但类对象可用更特殊的被赋予
Class
类型。

ClassaClass=[anObjectclass];

ClassrectClass=[Rectangleclass];

所有类对象的类型都是
Class
.
未完还在整理中。。。
类对象是一种成熟完备的对象,可以动态的赋予类型,接收消息,并从其它类继承方法。它们的独特之处仅在于它们是有编译器创建的。缺乏它们自己的数据结构(实例变量)而非类的生命中构建的,运行时类变量还是用来生成实例的代理。

注意:编译器也会为每个类构建元类对象。元类对象能够描述类对象,就像类对象可以描述类的实例一样。但是当你给实例与类变量发送消息的时候,元类对象仅为运行时系统内部使用。

创建实例

类对象的一个主要功能就是穿件新的实例。下面的代码告诉
Rectangle
类去创建一个新的矩形实例,并将其赋给
myRectangle
变量:

idmyRectangle;

myRectangle=[Rectanglealloc];

alloc方法会为新对象的实例变量分配一块内存并把它们都初始化为0----这里说的它们不包括用于连接新实例与它所属类的isa变量。要让一个对象更有作用,通常需要更完好的初始化。而这就是init方法的职能。初始化的工作一般会紧随分配工作之后进行。

myRectangle=[[Rectanglealloc]init];

在myRectangle能够接收任何前面章节提到的消息之前,需要先执行上面的那行代码。
alloc
方法返回了一个新的实例,接着那个实例执行了一个
init
方法以设置其初始化的状态。每个类对象都有至少一个方法(比如
alloc
)
使其能够创建新的对象,并且每个实例都有至少一个方法(比如
init
)使其准备好以备使用。初始化的方法通常会带一些参数以传一些特定的值,当然还有关键词作为参数名。(
initWithPosition:size:
,
例如这个,它是一个可以初始化一个新的
Rectangle实例的方法
),但每个初始化方法都是以“
init
”开头。


定制类对象

对于Objective-C语言磊说累呗当做对象来对待可不是什么突发奇想的事情。它是一种有计划的,有时候让人觉得不可思议,对设计来说获益颇多。我们可以为一个类定制一个对象,该类属于一个开放的集合。在AppKit里,举个例子,一个
NSMatrix
对象可以用一个特定类型的
NSCell
对象进行定制。
NSMatrix
对象可以负责创建可以代表其单元的独特对象。它可以在matrix是第一次初始化,并在晚些时候需要新的单元时执行这个操作。
NSMatrix
对象在屏幕上绘制的可见的
matrix在运行时是可以伸缩的,或许是对用户的操作的响应。当它扩展时,matrix需要能够创建新的对象来填充新加的位置。
但它们是什么样的对象呢?每个matrix只显示一种
NSCell
,但有很多种不同的类型。在图1-3的继承树中向我们展示了AppKit.中提供的其中一些类型。所有的都继承自
NSCell
类.



图1-3NSCell的继承树
当一个matrix创建了
NSCell
对象,应该是
NSButtonCell
对象来显示一块按钮或者切换按钮,还是
NSTextFieldCell
对象来显示一个文本框,用户可以在其中输入或者编辑文本,再或者是NSCell其它别的类型。
NSMatrix
对象必须能接纳任何类型的单元,即便是还没创造出来的。
这个问题的其中一种解决方案是定义一个
NSMatrix
抽象类,并且要求所有使用它的去声明一个子类,并实现用来创建新单元的方法。因为需要实现那些方法,所以用户可以确信它们创建的对象是正确的类型。
但是这个方案需要
NSMatrix
类的用户,这会给扩展带来诸多不便,并且很多开发处于不同项目中,可能会出现重复的开发工作,最后事情变的一团糟。
一个更好的方案是,让
NSMatrix
类适配,允许
NSMatrix
实例被某种
NSCell
实例化,也就是说,用一个类对象。
NSMatrix
类也可以定义一个
setCellClass:
方法,它可以为
NSCell
对象传递类对象,而
NSMatrix
对象将用来填充空的位置。

[myMatrixsetCellClass:[NSButtonCellclass]];

NSMatrix
对象使用类对象类产生新的单元,当它们被首次初始化并且它的容量需要扩大时。这种定制化在下述情况中可能会有不同,就是当类不是那种可以在消息中传送的并赋给一个变量的对象时。

变量和类对象

但你定义了一个新类时,你可以指定一些实例变量。每个类的实例都能维护属于它自己的一份变量副本---每个对象都控制它自己的数据。然而,没有对应于实例变量的类变量。只有初始化自类的定义并未类提供的内部数据结构。再有就是,一个类对象是不能访问任何实例的实例变量的,类变量不能初始化,读取或者改变它们。
对于所有的类的实例来说,要分享数据,你就必须定义一个外部的某种变量。最简单的方法就是在类的实现文件中声明一个变量。

intMCLSGlobalVariable;


@implementationMyClass

//implementationcontinues

在更好的视线中,你可以将一个变量声明为静态的,并提供类方法来管理它。变量声明成静态会把它的有效域限制在该类,而且仅仅是文件中实现它的类的那部分。(因此不像实例变量,静态变量不能被继承或者被子类直接操作)这种模式通常被用于定义可分享的类的实例。(例如单例,详情请参考Cocoa
FundamentalsGuide的“Creating
aSingletonInstance”部分).

staticMyClass*MCLSSharedInstance;


@implementationMyClass


+(MyClass*)sharedInstance

{

//checkforexistenceofsharedinstance

//createifnecessary

returnMCLSSharedInstance;

}

//implementationcontinues

静态变量为类对象提供了更多的功能,而非仅仅是一个生产实例的工厂。它能以自己的方式成为一个完整,万能的对象。类对象可以用于调整它创建的实例,从已经创建的对象列表中分配实例,或者管理其它重要的应用进程。当你只需要特定类的一个对象时,你可以把所有的对象的状态放入静态变量中,并只使用类方法。这会为你省去分配,初始化实例的步骤。

注意:你也可以使用没有声明为静态的外部变量,但是静态变量的有效域限制可以很好的为将数据封装到不同的对象这个目的服务。

初始化一个类变量

如果你想使用一个类变量,除了要分配实例外,你可能还需要把它初始化。尽管程序不会分配类变量,但Objective-C确实为我们提供一个让程序可以初始化它们的方法。
如果一个类使用了静态或者全局变量,
initialize
是个好方法来设置它们的初始值。例如:如果一个类维护了一个实例数组,
initialize
方法可以设置这个数组,升值为其分配一个或者两个默认实例以备使用。
在类的父类接收到
initialize后,并在该类接收到任何其它消息之前,运行时系统会给每个类对象发送一个initialize方法。这样就给类一些时间让它们可以在被使用前有机会对其运行环境进行设置。如果没有初始化的要求,你就不必写一个
initialize
方法来对消息进行响应。
由于继承,发送给一个没有实现
initialize
方法的类的
initialize
消息将转发给父类,即使父类已经接收到过
initialize
方法。例如,假设类A实现了
initialize
方法,类B继承了类A,但类B并没有实现
initialize
方法。在类B接收到它的第一条消息前,运行时系统会发送一个
initialize
方法给它。但是,由于类B并没有实现
initialize
,所以将取而代之由类A的
initialize
方法来执行。因此,类A需要确保它的初始化逻辑只执行依此,并为恰当的类执行。
为了避免多次执行初始化逻辑,在实现
initialize
方法时可以使用下面的模板1-1

Listing1-1
initialize
方法的实现

+(void)initialize

{

if(self==[ThisClassclass]){

//Performinitializationhere.

...

}

}

注意:要记住运行时系统会给每个类单独发送
initialize
方法。因此,你不能给它的父类发送
initialize
消息。

根类的方法

所有的对象,类以及实例等等,需要有一个面向运行时的借口。类对象跟实例应当能够对它们的能力进行自省,并报告它们在继承树中的位置。而提供这种借口正是
NSObject
类的职责所在。
所以说
NSObject
方法没必要被实现两次,一次用来为实例提供运行时借口,又一次为类对象而重复那个接口。类对象被特许执行定义在根类中的实例方法。当一个类对象收到一个它无法以它的类方法进行应答的消息时,运行时系统会决定是否有一个根实例方法能够进行应答。类对象只能在没有类对象可以执行某项任务,才能也仅能执行定义在根类中的实例方法。
如果向了解更多有关类对象执行根类实例方法的独特能力,可以参考NSObject
ClassReference.


源代码中的类名

在源代码中,类名可以用于两个不同的上下文环境。这些上下文环境反映出类的双重身份:数据类型以及对象。

类名可以用于作为某种对象的类型名,例如:

Rectangle*anObject;

这里
anObject
静态的赋予了类型,作为一个指向
Rectangle
对象的指针.
编译器会认为它拥有
Rectangle
实例的数据结构,定义及继承自
Rectangle
类的实例方法。静态赋予类型使编译器可以做更好的类型检查并使源代码自身更加一目了然。更多信息可以参考“Enabling
StaticBehavior”。
只有实例才可以被静态赋予类型,类对象是不可以的,因为它们不是一个类的成员,但属于
Class
数据类型.

作为消息表达式的***,类名代表着类对象。这个用法在之前的例子中都有提到。类名仅在作为消息***时可以代表类对象。在其它的任何上下文环境中,你必须请求类对象来获取它的
id
(通过给它发送一个
class
消息).
下面的例子把
Rectangle
类作为参数放在了一个
isKindOfClass:
消息中:

if([anObjectisKindOfClass:[Rectangleclass]])

...

直接使用简单的使用Rectangle这个名字作为参数是非法的。类名只能作为***。
如果你在编译时不知道类名,而是在运行时作为一个字符串获取到,那么你可以使用
NSClassFromString
来返回类对象。


NSString*className;

...

if([anObjectisKindOfClass:NSClassFromString(className)])

...

如果它传的字符串不是有效的类名的话这个方法将返回nil。

类名存在跟全局变量以及函数名存在于相同的命名空间。一个类跟全局变量不能重名。在Objective-C中类名是唯一具有全局可见的名字。


测试类是否相同

你可以直接使用指针来比较,测试两个类对象,看一下它们是否相同。很重要的一点是,要获取正确的类。在Cocoa框架中有几种特性,动态,透明的子类扩展了已经存在的类以扩展它们的功能。(例如KVO机制,以及CoreData框架就是这样的,可以参考Key-Value
ObservingProgrammingGuide跟Core
DataProgrammingGuide)在一个动态创建的子类中,类方法通常会被重写,所以子类会装饰成被它取代的类。在测试两个类是否相等时,你需要比较类方法返回的值而非那些低级方法返回的值。按照API的条款,下面的不等式适用于动态子类。

[objectclass]!=object_getClass(object)!=*((Class*)object)

因此你应该用下面的方法测试两个类是否相等

if([objectAclass]==[objectBclass]){//...

原文:

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


Objects,Classes,andMessaging

Thischapterdescribesthefundamentalsofobjects,classes,andmessagingasusedandimplementedbytheObjective-Clanguage.ItalsointroducestheObjective-Cruntime.


TheRuntimeSystem

TheObjective-Clanguagedefersasmanydecisionsasitcanfromcompiletimeandlinktimetoruntime.Wheneverpossible,itdynamicallyperformsoperations
suchascreatingobjectsanddeterminingwhatmethodtoinvoke.Therefore,thelanguagerequiresnotjustacompiler,butalsoaruntimesystemtoexecutethecompiledcode.TheruntimesystemactsasakindofoperatingsystemfortheObjective-Clanguage;
it’swhatmakesthelanguagework.Typically,however,youdon’tneedtointeractwiththeruntimedirectly.Tounderstandmoreaboutthefunctionalityitoffers,though,seeObjective-C
RuntimeProgrammingGuide.


Objects

Asthenameimplies,object-orientedprogramsarebuiltaroundobjects.Anobjectassociatesdatawiththeparticularoperationsthatcanuseoraffectthatdata.Objective-Cprovidesadata
typetoidentifyanobjectvariablewithoutspecifyingaparticularclassoftheobject.


ObjectBasics

Anobjectassociatesdatawiththeparticularoperationsthatcanuseoraffectthatdata.InObjective-C,theseoperationsareknownastheobject’smethods;
thedatatheyaffectareitsinstancevariables(in
otherenvironmentstheymaybereferredtoasivarsormembervariables).Inessence,anobjectbundlesadatastructure(instancevariables)andagroupofprocedures(methods)
intoaself-containedprogrammingunit.
InObjective-C,anobject’sinstancevariablesareinternaltotheobject;generally,yougetaccesstoan
object’sstateonlythroughtheobject’smethods(youcanspecifywhethersubclassesorotherobjectscanaccessinstancevariablesdirectlybyusingscopedirectives,see“The
ScopeofInstanceVariables”).Forotherstofindoutsomethingaboutanobject,therehastobeamethodtosupplytheinformation.Forexample,arectanglewouldhavemethodsthatrevealitssizeandposition.
Moreover,anobjectseesonlythemethodsthatweredesignedforit;itcan’tmistakenlyperformmethodsintendedforothertypesofobjects.JustasaCfunctionprotectsitslocalvariables,hidingthemfrom
therestoftheprogram,anobjecthidesbothitsinstancevariablesanditsmethodimplementations.


id

InObjective-C,objectidentifiersareofadistinctdatatype:
id
.
Thistypeisthegeneraltypeforanykindofobjectregardlessofclassandcanbeusedforinstancesofaclassandforclassobjectsthemselves.

idanObject;

Fortheobject-orientedconstructsofObjective-C,suchasmethodreturnvalues,
id
replaces
int
as
thedefaultdatatype.(ForstrictlyCconstructs,suchasfunctionreturnvalues,
int
remainsthedefaulttype.)
Thekeyword
nil
isdefined
asanullobject,an
id
withavalueof
0
.
id
,
nil
,
andtheotherbasictypesofObjective-Caredefinedintheheaderfile
objc/objc.h
.
id
isdefinedaspointertoanobjectdatastructure:

typedefstructobjc_object{

Classisa;

}*id;

Everyobjectthushasan
isa
variablethattellsitofwhatclassitisaninstance.Sincethe
Class
type
isitselfdefinedasapointer:

typedefstructobjc_class*Class;

the
isa
variableisfrequentlyreferredtoasthe“
isa
pointer.”


DynamicTyping

The
id
typeiscompletelynonrestrictive.Byitself,ityieldsnoinformationaboutanobject,exceptthatitisanobject.
Atsomepoint,aprogramtypicallyneedstofindmorespecificinformationabouttheobjectsitcontains.Sincethe
id
typedesignatorcan’tsupplythisspecificinformation
tothecompiler,eachobjecthastobeabletosupplyitatruntime.
The
isa
instancevariable
identifiestheobject’sclass—whatkindofobjectitis.Objectswiththesamebehavior(methods)andthesamekindsofdata(instancevariables)
aremembersofthesameclass.
Objectsarethusdynamicallytypedatruntime.Wheneveritneedsto,theruntimesystemcanfindtheexactclassthatanobjectbelongsto,justbyaskingtheobject.(Tolearnmoreaboutthe
runtime,seeObjective-CRuntime
ProgrammingGuide.)DynamictypinginObjective-Cservesasthefoundationfordynamicbinding,discussedlater.
The
isa
variablealsoenablesobjectstoperformintrospection—to
findoutaboutthemselves(orotherobjects).Thecompilerrecordsinformationaboutclassdefinitionsindatastructuresfortheruntimesystemtouse.Thefunctionsoftheruntimesystemuse
isa
to
findthisinformationatruntime.Usingtheruntimesystem,youcan,forexample,determinewhetheranobjectimplementsaparticularmethodordiscoverthenameofitssuperclass.
Objectclassesarediscussedinmoredetailunder“Classes.”
It’salsopossibletogivethecompilerinformationabouttheclassofanobjectbystaticallytypingitinsourcecodeusingtheclassname.Classesareparticularkindsofobjects,andtheclassnamecanserve
asatypename.See“Class
Types”and“Enabling
StaticBehavior.”


MemoryManagement

Inanyprogram,itisimportanttoensurethatobjectsaredeallocatedwhentheyarenolongerneeded—otherwiseyourapplication’smemoryfootprintbecomeslargerthannecessary.Itisalsoimportanttoensure
thatyoudonotdeallocateobjectswhilethey’restillbeingused.
Objective-Coffersthreemechanismsformemorymanagementthatallowyoutomeetthesegoals:

AutomaticReferenceCounting(ARC),wherethecompilerreasonsaboutthelifetimesofobjects.

ManualReferenceCounting(MRC,sometimesreferredtoasMRRfor“manualretain/release”),whereyouareultimatelyresponsiblefordeterminingthelifetimeofobjects.
ManualreferencecountingisdescribedinAdvanced
MemoryManagementProgrammingGuide.

Garbagecollection,whereyoupassresponsibilityfordeterminingthelifetimeofobjectstoanautomatic“collector.”
GarbagecollectionisdescribedinGarbage
CollectionProgrammingGuide.(NotavailableforiOS—youcannotaccessthisdocumentthroughtheiOSDevCenter.)


ObjectMessaging

Thissectionexplainsthesyntaxofsendingmessages,includinghowyoucannestmessageexpressions.Italsodiscussesthescopeor“visibility”ofanobject’sinstancevariables,andtheconceptsofpolymorphism
anddynamicbinding.


MessageSyntax

Togetanobjecttodosomething,yousenditamessagetelling
ittoapplyamethod.InObjective-C,messageexpressionsare
enclosedinbrackets:

[receivermessage]

Thereceiverisanobject,andthemessagetellsitwhattodo.Insourcecode,themessage
issimplythenameofamethodandanyparametersthatarepassedtoit.Whenamessageissent,theruntimesystemselectstheappropriatemethodfromthe
receiver’srepertoireandinvokesit.
Forexample,thismessagetellsthe
myRectangle
objecttoperformits
display
method,
whichcausestherectangletodisplayitself:

[myRectangledisplay];

Themessageisfollowedbya“
;
”asisnormalforanystatementinC.
Becausethemethodnameinamessageservesto“select”amethodimplementation,methodnamesinmessagesareoftenreferredtoasselectors.
Methodscanalsotakeparameters,sometimescalledarguments.Amessagewithasingleparameteraffixes
acolon(
:
)tothenameandputstheparameterrightafterthecolon:

[myRectanglesetWidth:20.0];

Formethodswithmultipleparameters,Objective-C'smethodnamesareinterleavedwiththeparameterssuchthatthemethod’snamenaturallydescribestheparametersexpectedbythemethod.Theimaginarymessage
belowtellsthe
myRectangle
objecttosetitsorigintothecoordinates(30.0,50.0):

[myRectanglesetOriginX:30.0y:50.0];//Thisisagoodexampleof

//multipleparameters

Aselectornameincludesallthepartsofthename,includingthecolons,sotheselectorintheprecedingexampleisnamed
setOriginX:y:
.
Ithastwocolons,becauseittakestwoparameters.Theselectornamedoesnot,however,includeanythingelse,suchasreturntypeorparametertypes.

ImportantThesubpartsofanObjective-Cselectornamearenotoptional,norcantheirorderbevaried.Insomelanguages,theterms“namedparameters”and“keywordparameters”carrytheimplications
thattheparameterscanvaryatruntime,canhavedefaultvalues,canbeinadifferentorder,andcanpossiblyhaveadditionalnamedparameters.NoneofthesecharacteristicsaboutparametersaretrueforObjective-C.
Forallintentsandpurposes,anObjective-CmethoddeclarationissimplyaCfunctionthatprependstwoadditionalparameters(see“Messaging”in
theObjective-CRuntimeProgramming
Guide).Thus,thestructureofanObjective-CmethoddeclarationdiffersfromthestructureofamethodthatusesnamedorkeywordparametersinalanguagelikePython,asthefollowingPythonexampleillustrates:

deffunc(a,b,NeatMode=SuperNeat,Thing=DefaultThing):

pass

InthisPythonexample,
Thing
and
NeatMode
mightbeomittedormighthavedifferentvalues
whencalled.

Inprinciple,a
Rectangle
classcouldinsteadimplementa
setOrigin::
method
withnolabelforthesecondparameter,whichwouldbeinvokedasfollows:

[myRectanglesetOrigin:30.0:50.0];//Thisisabadexampleofmultipleparameters

Whilesyntacticallylegal,
setOrigin::
doesnotinterleavethemethodnamewiththeparameters.Thus,thesecondparameteris
effectivelyunlabeledanditisdifficultforareaderofthiscodetodeterminethekindorpurposeofthemethod’sparameters.
Methodsthattakeavariablenumberofparametersarealsopossible,thoughthey’resomewhatrare.Extraparameters
areseparatedbycommasaftertheendofthemethodname.(Unlikecolons,thecommasarenotconsideredpartofthename.)Inthefollowingexample,theimaginary
makeGroup:
method
ispassedonerequiredparameter(
group
)andthreeparametersthatareoptional:

[receivermakeGroup:group,memberOne,memberTwo,memberThree];

LikestandardCfunctions,methodscanreturnvalues.Thefollowingexamplesetsthevariable
isFilled
to
YES
if
myRectangle
is
drawnasasolidrectangle,or
NO
ifit’sdrawninoutlineformonly.

BOOLisFilled;

isFilled=[myRectangleisFilled];

Notethatavariableandamethodcanhavethesamename.
Onemessageexpressioncanbenestedinsideanother.Here,thecolorofonerectangleissettothecolorofanother:

[myRectanglesetPrimaryColor:[otherRectprimaryColor]];

Objective-Calsoprovidesadot(
.
)operatorthatoffersacompactandconvenientsyntaxforinvokinganobject’saccessormethods.
Thedotoperatorisoftenusedinconjunctionwiththedeclaredpropertiesfeature(see“Declared
Properties”)andisdescribedin“Dot
Syntax.”


SendingMessagestonil

InObjective-C,itisvalidtosendamessageto
nil
—itsimplyhasnoeffectatruntime.ThereareseveralpatternsinCocoa
thattakeadvantageofthisfact.Thevaluereturnedfromamessageto
nil
may
alsobevalid:

Ifthemethodreturnsanobject,thenamessagesentto
nil
returns
0
(
nil
).
Forexample:

Person*motherInLaw=[[aPersonspouse]mother];

Ifthe
spouse
objecthereis
nil
,
then
mother
issentto
nil
andthemethodreturns
nil
.

Ifthemethodreturnsanypointertype,anyintegerscalarofsizelessthanorequalto
sizeof(void*)
,
a
float
,a
double
,a
long
double
,ora
longlong
,thenamessagesentto
nil
returns
0
.

Ifthemethodreturnsa
struct
,asdefinedbytheMac
OSXABIFunctionCallGuidetobereturnedinregisters,thenamessagesentto
nil
returns
0.0
for
everyfieldinthe
struct
.Other
struct
datatypeswillnotbefilledwithzeros.

Ifthemethodreturnsanythingotherthantheaforementionedvaluetypes,thereturnvalueofamessagesentto
nil
is
undefined.

Thefollowingcodefragmentillustratesavaliduseofsendingamessageto
nil
.

idanObjectMaybeNil=nil;


//thisisvalid

if([anObjectMaybeNilmethodThatReturnsADouble]==0.0)

{

//implementationcontinues...

}


TheReceiver’sInstanceVariables

Amethodhasautomaticaccesstothereceivingobject’sinstancevariables.
Youdon’tneedtopassthemtothemethodasparameters.Forexample,the
primaryColor
methodillustratedabovetakesnoparameters,yetitcanfindtheprimarycolorfor
otherRect
and
returnit.Everymethodassumesthereceiveranditsinstancevariables,withouthavingtodeclarethemasparameters.
ThisconventionsimplifiesObjective-Csourcecode.Italsosupportsthewayobject-orientedprogrammersthinkaboutobjectsandmessages.Messagesaresenttoreceiversmuchaslettersaredeliveredtoyour
home.Messageparametersbringinformationfromtheoutsidetothereceiver;theydon’tneedtobringthereceivertoitself.
Amethodhasautomaticaccessonlytothereceiver’sinstancevariables.Ifitrequiresinformationaboutavariablestoredinanotherobject,itmustsendamessagetotheobjectaskingittorevealthecontents
ofthevariable.The
primaryColor
and
isFilled
methodsshownearlierareusedforjustthis
purpose.
See“Defining
aClass”formoreinformationonreferringtoinstancevariables.


Polymorphism

Astheearlierexamplesillustrate,messagesinObjective-CappearinthesamesyntacticpositionsasfunctioncallsinstandardC.But,becausemethods“belongto”anobject,messagesdon’tbehaveinthesame
waythatfunctioncallsdo.
Inparticular,anobjectcanbeoperatedonbyonlythosemethodsthatweredefinedforit.Itcan’tconfusethemwithmethodsdefinedforotherkindsofobject,evenifanotherobjecthasamethodwiththesame
name.Therefore,twoobjectscanresponddifferentlytothesamemessage.Forexample,eachkindofobjectthatreceivesa
display
messagecoulddisplayitselfinaunique
way.A
Circle
anda
Rectangle
wouldresponddifferentlytoidenticalinstructionstotrack
thecursor.
Thisfeature,referredtoaspolymorphism,playsasignificantroleinthedesignofobject-oriented
programs.Togetherwithdynamicbinding,itpermitsyoutowritecodethatmightapplytoanynumberofdifferentkindsofobjects,withoutyouhavingtochooseatthetimeyouwritethecodewhatkindsofobjectstheymightbe.Theymightevenbeobjects
thatwillbedevelopedlater,byotherprogrammersworkingonotherprojects.Ifyouwritecodethatsendsa
display
messagetoan
id
variable,
anyobjectthathasa
display
methodisapotentialreceiver.


DynamicBinding

Acrucialdifferencebetweenfunctioncallsandmessagesisthatafunctionanditsparametersarejoined
togetherinthecompiledcode,butamessageandareceivingobjectaren’tuniteduntiltheprogramisrunningandthemessageissent.Therefore,theexactmethodinvokedtorespondtoamessagecanbedeterminedonlyatruntime,notwhenthecodeiscompiled.
Whenamessageissent,aruntimemessagingroutinelooksatthereceiverandatthemethodnamedinthemessage.Itlocatesthereceiver’simplementationofamethodmatchingthename,“calls”themethod,and
passesitapointertothereceiver’sinstancevariables.(Formoreonthisroutine,see“Messaging”inObjective-C
RuntimeProgrammingGuide.)
Thisdynamicbindingofmethodstomessagesworkshandinhandwithpolymorphismtogiveobject-orientedprogrammingmuchofitsflexibilityandpower.Becauseeachobjectcanhaveitsownversion
ofamethod,anObjective-Cstatementcanachieveavarietyofresults,notbyvaryingthemessagebutbyvaryingtheobjectthatreceivesthemessage.Receiverscanbedecidedastheprogramruns;thechoiceofreceivercanbemadedependentonfactorssuch
asuseractions.
WhenexecutingcodebasedupontheApplicationKit(AppKit),forexample,usersdeterminewhichobjectsreceivemessagesfrommenucommandssuchasCut,Copy,andPaste.Themessagegoestowhatever
objectcontrolsthecurrentselection.Anobjectthatdisplaystextwouldreacttoa
copy
messagedifferentlyfromanobjectthatdisplaysscannedimages.Anobjectthatrepresents
asetofshapeswouldresponddifferentlytoa
copy
messagethana
Rectangle
would.Because
messagesdonotselectmethodsuntilruntime(fromanotherperspective,becausebindingofmethodstomessagesdoesnotoccuruntilruntime),thesedifferencesinbehaviorareisolatedtothemethodsthemselves.Thecodethatsendsthemessagedoesn’thave
tobeconcernedwiththem;itdoesn’tevenhavetoenumeratethepossibilities.Anapplication’sobjectscaneachrespondinitsownwayto
copy
messages.
Objective-Ctakesdynamicbindingonestepfurtherandallowseventhemessagethat’ssent(themethodselector)tobeavariabledetermined
atruntime.Thismechanismisdiscussedinthesection“Messaging”inObjective-C
RuntimeProgrammingGuide.


DynamicMethodResolution

Youcanprovideimplementationsofclassandinstancemethodsatruntimeusingdynamicmethodresolution.See“Dynamic
MethodResolution”inObjective-C
RuntimeProgrammingGuideformoredetails.


DotSyntax

Objective-Cprovidesadot(
.
)operatorthatoffersanalternativetosquarebracketnotation(
[]
)
toinvokeaccessormethods.DotsyntaxusesthesamepatternthataccessingCstructureelementsuses:

myInstance.value=10;

printf("myInstancevalue:%d",myInstance.value);

Whenusedwithobjects,however,dotsyntaxactsas“syntacticsugar”—itistransformedbythecompilerintoaninvocationofanaccessormethod.Dotsyntaxdoesnotdirectlygetorsetaninstancevariable.
Thecodeexampleaboveisexactlyequivalenttothefollowing:

[myInstancesetValue:10];

printf("myInstancevalue:%d",[myInstancevalue]);

Asacorollary,ifyouwanttoaccessanobject’sowninstancevariableusingaccessormethods,youmustexplicitlycallout
self
,
forexample:

self.age=10;

ortheequivalent:

[selfsetAge:10];

Ifyoudonotuse
self.
,youaccesstheinstancevariabledirectly.Inthefollowingexample,thesetaccessormethodfor
age
isnotinvoked:

age=10;

Ifa
nil
valueisencounteredduringpropertytraversal,theresultisthesameassendingtheequivalentmessageto
nil
.
Forexample,thefollowingpairsareallequivalent:

//Eachmemberofthepathisanobject.

x=person.address.street.name;

x=[[[personaddress]street]name];


//ThepathcontainsaCstruct.

//Thiswillcrashifwindowisnilor-contentViewreturnsnil.

y=window.contentView.bounds.origin.y;

y=[[windowcontentView]bounds].origin.y;


//Anexampleofusingasetter.

person.address.street.name=@"OxfordRoad";

[[[personaddress]street]setName:@"OxfordRoad"];


Classes

Anobject-orientedprogramistypicallybuiltfromavarietyofobjects.AprogrambasedontheCocoaframeworks
mightuse
NSMatrix
objects,
NSWindow
objects,
NSDictionary
objects,
NSFont
objects,
NSText
objects,
andmanyothers.Programsoftenusemorethanoneobjectofthesamekindorclass—several
NSArray
objectsor
NSWindow
objects,
forexample.
InObjective-C,youdefineobjectsbydefiningtheirclass.Theclassdefinitionisaprototypeforakindofobject;itdeclarestheinstancevariablesthatbecomepartofeverymemberoftheclass,anditdefines
asetofmethodsthatallobjectsintheclasscanuse.
Thecompilercreatesjustoneaccessibleobjectforeachclass,aclassobjectthatknows
howtobuildnewobjectsbelongingtotheclass.(Forthisreasonit’straditionallycalledafactoryobject.)Theclassobjectisthecompiledversionoftheclass;
theobjectsitbuildsareinstancesof
theclass.Theobjectsthatdothemainworkofyourprogramareinstancescreatedbytheclassobjectatruntime.
Allinstancesofaclasshavethesamesetofmethods,andtheyallhaveasetofinstancevariablescutfromthesamemold.Eachobjectgetsitsowninstancevariables,butthemethodsareshared.
Byconvention,classnamesbeginwithanuppercaseletter(suchas
Rectangle
);thenamesofinstancestypicallybeginwitha
lowercaseletter(suchas
myRectangle
).


Inheritance

Classdefinitionsareadditive;eachnewclassthatyoudefineisbasedonanotherclassfrom
whichitinheritsmethodsandinstancevariables.Thenewclasssimplyaddstoormodifieswhatitinherits.Itdoesn’tneedtoduplicateinheritedcode.
Inheritancelinksallclassestogetherinahierarchicaltreewithasingleclassatitsroot.
WhenwritingcodethatisbasedupontheFoundationframework,thatrootclassistypically
NSObject
.
Everyclass(exceptarootclass)hasasuperclassone
stepnearertheroot,andanyclass(includingarootclass)canbethesuperclassforanynumberofsubclassesone
stepfartherfromtheroot.Figure1-1illustratesthehierarchyforafewoftheclassesusedinadrawingprogram.
Figure
1-1Somedrawingprogramclasses


Figure1-1showsthatthe
Square
class
isasubclassofthe
Rectangle
class,the
Rectangle
classisasubclassof
Shape
,
Shape
is
asubclassof
Graphic
,and
Graphic
isasubclassof
NSObject
.
Inheritanceiscumulative.Soa
Square
objecthasthemethodsandinstancevariablesdefinedfor
Rectangle
,
Shape
,
Graphic
,
and
NSObject
,aswellasthosedefinedspecificallyfor
Square
.Thisissimplytosaythat
anobjectoftype
Square
isn’tonlyasquare,it’salsoarectangle,ashape,agraphic,andanobjectoftype
NSObject
.
Everyclassbut
NSObject
canthusbeseenasaspecializationor
anadaptationofanotherclass.Eachsuccessivesubclassfurthermodifiesthecumulativetotalofwhat’sinherited.The
Square
class
definesonlytheminimumneededtoturnarectangleintoasquare.
Whenyoudefineaclass,youlinkittothehierarchybydeclaringitssuperclass;everyclassyoucreatemustbethesubclassofanotherclass(unlessyoudefineanewrootclass).Plentyofpotentialsuperclasses
areavailable.Cocoaincludesthe
NSObject
classandseveralframeworkscontainingdefinitionsformorethan250additionalclasses.Someareclassesthatyoucanuseoffthe
shelfandincorporatethemintoyourprogramasis.Othersyoumightwanttoadapttoyourownneedsbydefiningasubclass.
Someframeworkclassesdefinealmosteverythingyouneed,butleavesomespecificstobeimplementedinasubclass.Youcanthuscreateverysophisticatedobjectsbywritingonlyasmallamountofcodeandreusing
workdonebytheprogrammersoftheframework.

TheNSObjectClass

NSObject
isarootclass,
andsodoesn’thaveasuperclass.ItdefinesthebasicframeworkforObjective-Cobjectsandobjectinteractions.Itimpartstotheclassesandinstancesofclassesthatinheritfromittheabilitytobehaveasobjectsandcooperatewiththeruntimesystem.
Aclassthatdoesn’tneedtoinheritanyspecialbehaviorfromanotherclassshouldneverthelessbemadeasubclassofthe
NSObject
class.
InstancesoftheclassmustatleasthavetheabilitytobehavelikeObjective-Cobjectsatruntime.Inheritingthisabilityfromthe
NSObject
classismuchsimplerandmuch
morereliablethanreinventingitinanewclassdefinition.

Note:Implementinganewrootclassisadelicatetaskandonewithmanyhiddenhazards.Theclassmustduplicatemuchofwhatthe
NSObject
class
does,suchasallocateinstances,connectthemtotheirclass,andidentifythemtotheruntimesystem.Forthisreason,youshouldgenerallyusethe
NSObject
classprovided
withCocoaastherootclass.Formoreinformation,seeNSObject
ClassReferenceandtheNSObject
ProtocolReference.

InheritingInstanceVariables

Whenaclassobjectcreatesanewinstance,thenewobjectcontainsnotonlytheinstancevariablesthatwere
definedforitsclassbutalsotheinstancevariablesdefinedforitssuperclassandforitssuperclass’ssuperclass,allthewaybacktotherootclass.Thus,the
isa
instance
variabledefinedinthe
NSObject
classbecomespartofeveryobject.
isa
connectseachobject
toitsclass.
Figure1-2showssomeoftheinstancevariablesthatcouldbedefinedforaparticular
implementationofa
Rectangle
classandwheretheymaycomefrom.Notethatthevariablesthatmaketheobjectarectangleareaddedtotheonesthatmakeitashape,andthe
onesthatmakeitashapeareaddedtotheonesthatmakeitagraphic,andsoon.
Figure
1-2Rectangleinstancevariables


Aclassdoesn’thavetodeclareinstancevariables.Itcansimplydefinenewmethodsandrelyontheinstance
variablesitinherits,ifitneedsanyinstancevariablesatall.Forexample,
Square
mightnotdeclareanynewinstancevariablesofitsown.

InheritingMethods

Anobjecthasaccessnotonlytothemethodsdefinedforitsclassbut
alsotomethodsdefinedforitssuperclass,andforitssuperclass’ssuperclass,allthewaybacktotherootofthehierarchy.Forinstance,a
Square
objectcanusemethods
definedinthe
Rectangle
,
Shape
,
Graphic
,
and
NSObject
classesaswellasmethodsdefinedinitsownclass.
Anynewclassyoudefineinyourprogramcanthereforemakeuseofthecodewrittenforalltheclassesaboveitinthehierarchy.Thistypeofinheritanceisamajorbenefitofobject-orientedprogramming.When
youuseoneoftheobject-orientedframeworksprovidedbyCocoa,yourprogramscantakeadvantageofthebasicfunctionalitycodedintotheframeworkclasses.Youhavetoaddonlythecodethatcustomizesthestandardfunctionalitytoyourapplication.
Classobjectsalsoinheritfromtheclassesabovetheminthehierarchy.Butbecausetheydon’thaveinstancevariables(onlyinstancesdo),theyinheritonlymethods.

OverridingOneMethodwithAnother

There’soneusefulexceptiontoinheritance:Whenyoudefineanewclass,youcanimplementanewmethodwiththesamenameasonedefinedinaclassfartherupthehierarchy.Thenewmethodoverridestheoriginal;
instancesofthenewclassperformitratherthantheoriginal,andsubclassesofthenewclassinherititratherthantheoriginal.
Forexample,
Graphic
definesa
display
method
that
Rectangle
overridesbydefiningitsownversionof
display
.The
Graphic
method
isavailabletoallkindsofobjectsthatinheritfromthe
Graphic
class—butnotto
Rectangle
objects,
whichinsteadperformthe
Rectangle
versionof
display
.
Althoughoverridingamethodblockstheoriginalversionfrombeinginherited,othermethodsdefinedinthenewclasscanskipovertheredefinedmethodandfindtheoriginal(see“Messages
toselfandsuper”tolearnhow).
Aredefinedmethodcanalsoincorporatetheverymethoditoverrides.Whenitdoes,thenewmethodservesonlytorefineormodifythemethoditoverrides,ratherthanreplaceitoutright.Whenseveralclasses
inthehierarchydefinethesamemethod,buteachnewversionincorporatestheversionitoverrides,theimplementationofthemethodiseffectivelyspreadoveralltheclasses.
Althoughasubclasscanoverrideinheritedmethods,itcan’toverrideinheritedinstancevariables.Becauseanobjecthasmemoryallocatedforeveryinstancevariableitinherits,youcan’toverrideaninherited
variablebydeclaringanewonewiththesamename.Ifyoutry,thecompilerwillcomplain.

AbstractClasses

Some
classesaredesignedonlyorprimarilysothatotherclassescaninheritfromthem.Theseabstractclassesgroupmethodsandinstancevariablesthatcanbeusedbyanumberofsubclassesintoacommondefinition.Theabstractclassistypically
incompletebyitself,butcontainsusefulcodethatreducestheimplementationburdenofitssubclasses.(Becauseabstractclassesmusthavesubclassestobeuseful,they’resometimesalsocalledabstractsuperclasses.)
Unlikesomeotherlanguages,Objective-Cdoesnothavesyntaxtomarkclassesasabstract,nordoesitpreventyoufromcreatinganinstanceofanabstractclass.
The
NSObject
classisthecanonicalexampleofanabstractclassinCocoa.Youneveruseinstancesofthe
NSObject
class
inanapplication—itwouldn’tbegoodforanything;itwouldbeagenericobjectwiththeabilitytodonothinginparticular.
The
NSView
class,ontheotherhand,providesanexampleofanabstractclass,instancesofwhichyoumightoccasionallyuse
directly.
Abstractclassesoftencontaincodethathelpsdefinethestructureofanapplication.Whenyoucreatesubclassesoftheseclasses,instancesofyournewclassesfiteffortlesslyintotheapplicationstructure
andworkautomaticallywithotherobjects.


ClassTypes

Aclassdefinitionisaspecificationforakindofobject.Theclass,ineffect,definesadatatype.Thetypeisbasednotjustonthedatastructuretheclassdefines(instancevariables),butalsoonthe
behaviorincludedinthedefinition(methods).
AclassnamecanappearinsourcecodewhereveratypespecifierispermittedinC—forexample,asanargumenttothe
sizeof
operator:

inti=sizeof(Rectangle);

StaticTyping

Youcanuseaclassnameinplaceof
id
todesignateanobject’stype:

Rectangle*myRectangle;

Becausethiswayofdeclaringanobjecttypegivesthecompilerinformationaboutthekindofobjectitis,it’sknownasstatictyping.
Justas
id
isactuallyapointer,objectsarestaticallytypedaspointers
toaclass.Objectsarealwaystypedbyapointer.Statictypingmakesthepointerexplicit;
id
hidesit.
Statictypingpermitsthecompilertodosometypechecking—forexample,towarnifanobjectcouldreceiveamessagethatitappearsnottobeabletorespondto—andtoloosensomerestrictionsthatapplyto
objectsgenericallytyped
id
.Inaddition,itcanmakeyourintentionsclearertootherswhoreadyoursourcecode.However,itdoesn’tdefeatdynamicbindingoralterthe
dynamicdeterminationofareceiver’sclassatruntime.
Anobjectcanbestaticallytypedtoitsownclassortoanyclassthatitinheritsfrom.Forexample,becauseinheritancemakesa
Rectangle
object
akindof
Graphic
object(asshownintheexamplehierarchyinFigure
1-1),a
Rectangle
instancecanbestaticallytypedtothe
Graphic
class:

Graphic*myRectangle;

Statictypingtothesuperclassispossibleherebecausea
Rectangle
objectisa
Graphic
object.
Inaddition,it’smorethanthatbecauseitalsohastheinstancevariablesandmethodcapabilitiesof
Shape
and
Rectangle
objects,
butit’sa
Graphic
objectnonetheless.Forpurposesoftypechecking,giventhedeclarationdescribedhere,thecompilerconsiders
myRectangle
to
beoftype
Graphic
.Atruntime,however,ifthe
myRectangle
objectisallocatedandinitialized
asaninstanceof
Rectangle
,itistreatedasone.
See“Enabling
StaticBehavior”formoreonstatictypinganditsbenefits.

TypeIntrospection

Instancescanrevealtheirtypesatruntime.The
isMemberOfClass:
method,
definedinthe
NSObject
class,checkswhetherthereceiverisaninstanceofaparticularclass:

if([anObjectisMemberOfClass:someClass])

...

The
isKindOfClass:
method,
alsodefinedinthe
NSObject
class,checksmoregenerallywhetherthereceiverinheritsfromorisamemberofaparticularclass(whetherithastheclassinitsinheritance
path):

if([anObjectisKindOfClass:someClass])

...

Thesetofclassesforwhich
isKindOfClass:
returns
YES
is
thesamesettowhichthereceivercanbestaticallytyped.
Introspectionisn’tlimitedtotypeinformation.Latersectionsofthischapterdiscussmethodsthatreturntheclassobject,reportwhetheranobjectcanrespondtoamessage,andrevealotherinformation.
SeeNSObject
ClassReferenceformoreon
isKindOfClass:
,
isMemberOfClass:
,andrelatedmethods.


ClassObjects

Aclassdefinitioncontainsvariouskindsofinformation,muchofitaboutinstancesoftheclass:

Thenameoftheclassanditssuperclass

Atemplatedescribingasetofinstancevariables

Thedeclarationsofmethodnamesandtheirreturnandparametertypes

Themethodimplementations

Thisinformationiscompiledandrecordedindatastructuresmadeavailabletotheruntimesystem.Thecompilercreatesjustoneobject,aclassobject,torepresenttheclass.Theclassobject
hasaccesstoalltheinformationabouttheclass,whichmeansmainlyinformationaboutwhatinstancesoftheclassarelike.It’sabletoproducenewinstancesaccordingtotheplanputforwardintheclassdefinition.
Althoughaclassobjectkeepstheprototypeofaclassinstance,it’snotaninstanceitself.Ithasnoinstancevariablesofitsownanditcan’tperformmethodsintendedforinstancesoftheclass.However,
aclassdefinitioncanincludemethodsintendedspecificallyfortheclassobject—classmethodsas
opposedtoinstancemethods.
Aclassobjectinheritsclassmethodsfromtheclassesaboveitinthehierarchy,justasinstancesinheritinstancemethods.
Insourcecode,theclassobjectisrepresentedbytheclassname.Inthefollowingexample,the
Rectangle
classreturnsthe
classversionnumberusingamethodinheritedfromthe
NSObject
class:

intversionNumber=[Rectangleversion];

However,theclassnamestandsfortheclassobjectonlyasthereceiverinamessageexpression.Elsewhere,youneedtoaskaninstanceortheclasstoreturntheclass
id
.
Bothrespondtoa
class
message:

idaClass=[anObjectclass];

idrectClass=[Rectangleclass];

Astheseexamplesshow,classobjectscan,likeallotherobjects,betyped
id
.
Butclassobjectscanalsobemorespecificallytypedtothe
Class
datatype:

ClassaClass=[anObjectclass];

ClassrectClass=[Rectangleclass];

Allclassobjectsareoftype
Class
.Usingthistypenameforaclassisequivalenttousingtheclassnametostaticallytype
aninstance.
Classobjectsarethusfull-fledgedobjectsthatcanbedynamicallytyped,receivemessages,andinheritmethodsfromotherclasses.They’respecialonlyinthatthey’recreatedbythecompiler,lackdatastructures
(instancevariables)oftheirownotherthanthosebuiltfromtheclassdefinition,andaretheagentsforproducinginstancesatruntime.

Note:Thecompileralsobuildsametaclassobjectforeachclass.Itdescribestheclass
objectjustastheclassobjectdescribesinstancesoftheclass.Butwhileyoucansendmessagestoinstancesandtotheclassobject,themetaclassobjectisusedonlyinternallybytheruntimesystem.

CreatingInstances

Aprincipalfunctionofaclassobjectistocreatenewinstances.
Thiscodetellsthe
Rectangle
classtocreateanewrectangleinstanceandassignittothe
myRectangle
variable:

idmyRectangle;

myRectangle=[Rectanglealloc];

The
alloc
methoddynamically
allocatesmemoryforthenewobject’sinstancevariablesandinitializesthemallto
0
—all,thatis,exceptthe
isa
variable
thatconnectsthenewinstancetoitsclass.Foranobjecttobeuseful,itgenerallyneedstobemorecompletelyinitialized.That’sthefunctionofan
init
method.
Initializationtypicallyfollowsimmediatelyafterallocation:

myRectangle=[[Rectanglealloc]init];

Thislineofcode,oronelikeit,wouldbenecessarybefore
myRectangle
couldreceiveanyofthemessagesthatwereillustrated
inpreviousexamplesinthischapter.The
alloc
methodreturnsanewinstanceandthatinstanceperformsan
init
method
tosetitsinitialstate.Everyclassobjecthasatleastonemethod(like
alloc
)thatenablesittoproducenewobjects,andeveryinstancehasatleastonemethod(like
init
)
thatpreparesitforuse.Initializationmethodsoftentakeparameterstoallowparticularvaluestobepassedandhavekeywordstolabeltheparameters(
initWithPosition:size:
,
forexample,isamethodthatmightinitializeanew
Rectangle
instance),buteveryinitializationmethodbeginswith“
init
”.

CustomizationwithClassObjects

It’snotjustawhimoftheObjective-Clanguagethatclassesaretreatedasobjects.It’sachoicethathas
intended,andsometimessurprising,benefitsfordesign.It’spossible,forexample,tocustomizeanobjectwithaclass,wheretheclassbelongstoanopen-endedset.InAppKit,forexample,an
NSMatrix
object
canbecustomizedwithaparticularkindof
NSCell
object.
An
NSMatrix
objectcantakeresponsibilityforcreatingtheindividualobjectsthatrepresentitscells.Itcandothiswhen
thematrixisfirstinitializedandlaterwhennewcellsareneeded.Thevisiblematrixthatan
NSMatrix
objectdrawsonthescreencangrowandshrinkatruntime,perhaps
inresponsetouseractions.Whenitgrows,thematrixneedstobeabletoproducenewobjectstofillthenewslotsthatareadded.
Butwhatkindofobjectsshouldtheybe?Eachmatrixdisplaysjustonekindof
NSCell
,buttherearemanydifferentkinds.The
inheritancehierarchyinFigure1-3showssomeofthoseprovidedbyAppKit.Allinheritfromthegeneric
NSCell
class.
Figure
1-3TheinheritancehierarchyforNSCell


Whenamatrixcreates
NSCell
objects,shouldtheybe
NSButtonCell
objects
todisplayabankofbuttonsorswitches,
NSTextFieldCell
objectstodisplayfieldswheretheusercanenterandedittext,orsomeotherkindof
NSCell
?
The
NSMatrix
objectmustallowforanykindofcell,eventypesthathaven’tbeeninventedyet.
Onesolutiontothisproblemwouldbetodefinethe
NSMatrix
classasabstractandrequireeveryonewhousesittodeclarea
subclassandimplementthemethodsthatproducenewcells.Becausetheywouldbeimplementingthemethods,userscouldmakecertainthattheobjectstheycreatedwereoftherighttype.
Butthissolutionwouldrequireusersofthe
NSMatrix
classtodoworkthatoughttobedoneinthe
NSMatrix
class
itself,anditunnecessarilyproliferatesthenumberofclasses.Becauseanapplicationmightneedmorethanonekindofmatrix,eachwithadifferentkindofcell,itcouldbecomeclutteredwith
NSMatrix
subclasses.
Everytimeyouinventedanewkindof
NSCell
,you’dalsohavetodefineanewkindof
NSMatrix
.
Moreover,programmersondifferentprojectswouldbewritingvirtuallyidenticalcodetodothesamejob,alltomakeupforthefailureof
NSMatrix
todoit.
Abettersolution,andthesolutionthe
NSMatrix
classadopts,istoallow
NSMatrix
instances
tobeinitializedwithakindof
NSCell
—thatis,withaclassobject.The
NSMatrix
class
alsodefinesa
setCellClass:
methodthatpassestheclassobjectforthekindof
NSCell
object
an
NSMatrix
shouldusetofillemptyslots:

[myMatrixsetCellClass:[NSButtonCellclass]];

The
NSMatrix
objectusestheclassobjecttoproducenewcellswhenit’sfirstinitializedandwheneverit’sresizedtocontain
morecells.Thiskindofcustomizationwouldbedifficultifclassesweren’tobjectsthatcouldbepassedinmessagesandassignedtovariables.

VariablesandClassObjects

Whenyoudefineanewclass,youcanspecifyinstancevariables.Everyinstanceoftheclasscanmaintainitsowncopyofthevariablesyoudeclare—eachobjectcontrolsitsowndata.Thereis,however,noclass
variablecounterparttoaninstancevariable.Onlyinternaldatastructures,initializedfromtheclassdefinition,areprovidedfortheclass.Moreover,a
classobjecthasnoaccesstotheinstancevariablesofanyinstances;itcan’tinitialize,read,oralterthem.
Foralltheinstancesofaclasstosharedata,youmustdefineanexternalvariableofsomesort.Thesimplestwaytodothisistodeclareavariableintheclassimplementationfile:

intMCLSGlobalVariable;


@implementationMyClass

//implementationcontinues

Inamoresophisticatedimplementation,youcandeclareavariabletobe
static
,andprovideclassmethodsto
manageit.Declaringavariable
static
limitsitsscopetojusttheclass—andtojustthepartoftheclassthat’simplementedinthefile.(Thusunlikeinstancevariables,
staticvariablescannotbeinheritedby,ordirectlymanipulatedby,subclasses.)Thispatterniscommonlyusedtodefinesharedinstancesofaclass(suchassingletons;see“Creating
aSingletonInstance”inCocoa
FundamentalsGuide).

staticMyClass*MCLSSharedInstance;


@implementationMyClass


+(MyClass*)sharedInstance

{

//checkforexistenceofsharedinstance

//createifnecessary

returnMCLSSharedInstance;

}

//implementationcontinues

Staticvariableshelpgivetheclassobjectmorefunctionalitythanjustthatofafactoryproducinginstances;itcanapproachbeingacompleteandversatileobjectinitsownright.Aclass
objectcanbeusedtocoordinatetheinstancesitcreates,dispenseinstancesfromlistsofobjectsalreadycreated,ormanageotherprocessesessentialtotheapplication.Inthecasewhenyouneedonlyoneobjectofaparticularclass,youcanputallthe
object’sstateintostaticvariablesanduseonlyclassmethods.Thissavesthestepofallocatingandinitializinganinstance.

Note:Itisalsopossibletouseexternalvariablesthatarenotdeclared
static
,butthelimitedscopeof
staticvariablesbetterservesthepurposeofencapsulatingdataintoseparateobjects.

InitializingaClassObject

Ifyouwanttouseaclassobjectfor
anythingbesidesallocatinginstances,youmayneedtoinitializeitjustasyouwouldaninstance.Althoughprogramsdon’tallocateclassobjects,Objective-Cdoesprovideawayforprogramstoinitializethem.
Ifaclassmakesuseofstaticorglobalvariables,the
initialize
methodisagoodplacetosettheirinitialvalues.Forexample,
ifaclassmaintainsanarrayofinstances,the
initialize
methodcouldsetupthearrayandevenallocateoneortwodefaultinstancestohavethemready.
Theruntimesystemsendsan
initialize
message
toeveryclassobjectbeforetheclassreceivesanyothermessagesandafteritssuperclasshasreceivedthe
initialize
message.Thissequencegivestheclassachancetoset
upitsruntimeenvironmentbeforeit’sused.Ifnoinitializationisrequired,youdon’tneedtowritean
initialize
methodtorespondtothemessage.
Becauseofinheritance,an
initialize
messagesenttoaclassthatdoesn’timplementthe
initialize
method
isforwardedtothesuperclass,eventhoughthesuperclasshasalreadyreceivedthe
initialize
message.Forexample,assumeclassAimplementsthe
initialize
method,
andclassBinheritsfromclassAbutdoesnotimplementthe
initialize
method.JustbeforeclassBistoreceiveitsfirstmessage,theruntimesystemsends
initialize
to
it.But,becauseclassBdoesn’timplement
initialize
,classA’s
initialize
isexecuted
instead.Therefore,classAshouldensurethatitsinitializationlogicisperformedonlyonce,andfortheappropriateclass.
Toavoidperforminginitializationlogicmorethanonce,usethetemplateinListing1-1when
implementingthe
initialize
method.

Listing1-1Implementationoftheinitializemethod

+(void)initialize

{

if(self==[ThisClassclass]){

//Performinitializationhere.

...

}

}

Note:Rememberthattheruntimesystemsends
initialize
toeachclassindividually.Therefore,inaclass’s
implementationofthe
initialize
method,youmustnotsendthe
initialize
messagetoits
superclass.

MethodsoftheRootClass

Allobjects,classesandinstancesalike,needaninterfacetotheruntimesystem.Bothclassobjectsandinstancesshouldbeabletointrospectabouttheirabilitiesandtoreporttheirplaceintheinheritance
hierarchy.It’stheprovinceofthe
NSObject
classtoprovidethisinterface.
Sothat
NSObject
methodsdon’thavetobeimplementedtwice—oncetoprovidearuntimeinterfaceforinstancesandagaintoduplicate
thatinterfaceforclassobjects—classobjectsaregivenspecialdispensationtoperforminstancemethodsdefinedintherootclass.Whenaclassobjectreceives
amessagethatitcan’trespondtowithaclassmethod,theruntimesystemdetermineswhetherthere’sarootinstancemethodthat
canrespond.Theonlyinstancemethodsthataclassobjectcanperformarethosedefinedintherootclass,andonlyifthere’snoclassmethodthatcandothejob.
Formoreonthispeculiarabilityofclassobjectstoperformrootinstancemethods,seeNSObject
ClassReference.


ClassNamesinSourceCode

Insourcecode,classnamescanbeusedinonlytwoverydifferentcontexts.Thesecontextsreflectthedual
roleofaclassasadatatypeandasanobject:

Theclassnamecanbeusedasatypenameforakindofobject.Forexample:

Rectangle*anObject;

Here
anObject
isstaticallytypedtobeapointertoa
Rectangle
object.
Thecompilerexpectsittohavethedatastructureofa
Rectangle
instanceandtohavetheinstancemethodsdefinedandinheritedbythe
Rectangle
class.
Statictypingenablesthecompilertodobettertypecheckingandmakessourcecodemoreself-documenting.See“Enabling
StaticBehavior”fordetails.
Onlyinstancescanbestaticallytyped;classobjectscan’tbe,becausetheyaren’tmembersofaclass,
butratherbelongtothe
Class
datatype.

Asthereceiverin
amessageexpression,theclassnamereferstotheclassobject.Thisusagewasillustratedinseveraloftheearlierexamples.Theclassnamecanstandfortheclassobjectonlyasamessagereceiver.Inanyothercontext,youmustasktheclassobjectto
revealits
id
(bysendingita
class
message).Thisexamplepassesthe
Rectangle
class
asaparameterinan
isKindOfClass:
message:

if([anObjectisKindOfClass:[Rectangleclass]])

...

Itwouldhavebeenillegaltosimplyusethename“Rectangle”astheparameter.Theclassnamecanonlybeareceiver.
Ifyoudon’tknowtheclassnameatcompiletimebuthaveitasastringatruntime,youcanuse
NSClassFromString
to
returntheclassobject:

NSString*className;

...

if([anObjectisKindOfClass:NSClassFromString(className)])

...

Thisfunctionreturns
nil
ifthestringit’spassedisnotavalidclassname.

Classnamesexistinthesamenamespaceasglobalvariablesandfunctionnames.Aclassandaglobalvariablecan’thavethesamename.ClassnamesaretheonlynameswithglobalvisibilityinObjective-C.


TestingClassEquality

Youcantesttwoclassobjectsforequalityusingadirectpointercomparison.Itisimportant,though,togetthecorrectclass.ThereareseveralfeaturesintheCocoaframeworksthatdynamicallyandtransparently
subclassexistingclassestoextendtheirfunctionality(forexample,key-valueobservingandCoreDatadothis—seeKey-Value
ObservingProgrammingGuideandCore
DataProgrammingGuiderespectively).Inadynamically-createdsubclass,the
class
method
istypicallyoverriddensuchthatthesubclassmasqueradesastheclassitreplaces.Whentestingforclassequality,youshouldthereforecomparethevaluesreturnedbythe
class
method
ratherthanthosereturnedbylower-levelfunctions.PutintermsofAPI,thefollowinginequalitiespertainfordynamicsubclasses:

[objectclass]!=object_getClass(object)!=*((Class*)object)

Youshouldthereforetesttwoclassesforequalityasfollows:

if([objectAclass]==[objectBclass]){//...

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