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; |
关键字nil用于定义一个空对象,一个值为0的id,nil,可以在objc/objc.h中找到其它类型的Objective-C基础类型定义。
下面的例子中id被定义为指向一个数据构造函数的指针。
typedefstructobjc_object{ |
Classisa; |
}*id; |
typedefstructobjc_class*Class; |
isa通常被成为“
isa指针.”
动态类型
id类型是完全不严格的。就它自身来说,它没有产生任何有关一个对象的信息,当然除了它是一个对象。从某种意义上来说,一个程序通常需要得到更多有关一个对象它所包含的特定信息。因为id类型标示符无法为编译器提供这种特定信息,这些对象必须在运行时能够提供出这些信息。
isa实例变量指明了对象的类,该对象是什么类型的对象。拥有相同的行为(方法)以及相同的数据(实例变量)的对象是属于相同的类。
这种对象是运行时的动态类型。只要需要,运行时系统随时都可以找到该对象所属的准确的类,只要通过询问该对象。(要想了解关于运行时的信息,可以看考Objective-C
RuntimeProgrammingGuide一文)。Objective-C中的动态类型是作为动态绑定的基础进行服务的,晚些时候我们会进一步进行讨论。
isa变量同样可以让对象进行自省操作--以找到关于它们自己的信息(或者其它对象)。编译器会记录供运行时系统使用的有关类在数据结构中的定义相关信息。使用运行时系统时,你可以决定是否一个对象实现了一个特定的方法或者查找它的父类的名字。
我们将在
它还可以通过静态的在源代码中输入类名来为编译器提供对象类的信息。类是特定类型的对象,并且类名是作为类型名来使用的。可以参考“Class
Types”和“Enabling
StaticBehavior.”
内存管理
在任何程序中,当对象不在使用时确保它们被正确释放都是极其重要的,否则你的应用的内存轨迹会超出它们实际需要的量。同样重要的是,我们应当确保不要去释放正在使用中的对象。
Objective-C提供了三种方式来让我们实现上述目标:
AutomaticReferenceCounting(ARC),自动引用统计,编译器会推断出所有对象的生命周期
ManualReferenceCounting(MRC),手动引用统计,有时也叫MRR,即手动保持,释放。用于你完全掌控并决定对象的生命周期。
详情可以参考AdvancedMemoryManagementProgramming
Guide.
Garbagecollection,垃圾回收,将决定对象生命周期的责任传递给自动回收器
可以参考
(但文档不适用于iOS—IOS的你可以通过访问IOS开发中心获取该文档.
对象消息
本节主要介绍了发送消息的语法,包括如何嵌套消息异常。我们还将讨论有关一个对象实例变量的可见域方面的问题。以及多态和动态绑定的概念。
发消息的语法
要让一个对象去做一些事情,你需要给它他送一个消息,告诉它去请求一个方法。在Objective-C中,消息异常被放在方括号中;[receivermessage] |
例如,下面这条消息告诉
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文档中的
因此,Objective-C方法声明的结构不同于那些使用named或者keyword参数的语言,比如Python,就像下面的Python例子所描述的:
deffunc(a,b,NeatMode=SuperNeat,Thing=DefaultThing): |
pass |
Thing和
NeatMode可能在被调用时被忽略或者有不同的值
在日常操作中,
Rectangle类可以简单的替代实现
setOrigin::方法,而没有第二个参数的任何标签,调用的时候如下面的例子:
[myRectanglesetOrigin:30.0:50.0];//Thisisabadexampleofmultipleparameters |
setOrigin::没有将方法名与参数交叉。因此第二个参数没有任何标签,但这样会让看这段代码的人很困惑,不知道这些参数是做什么的。
一个方法有可变参数也是可能的,尽管这种情况很少出现。额外的参数用逗号分开。在下面的例子中
makeGroup:方法传递了一个必须的参数(
group)一起另外三个可选的参数:
[receivermakeGroup:group,memberOne,memberTwo,memberThree]; |
BOOLisFilled; |
isFilled=[myRectangleisFilled]; |
一个消息表达式可以嵌套在另外一个消息的内部。下面的例子是一个矩形的颜色设置给了另外一个矩形的颜色。
[myRectanglesetPrimaryColor:[otherRectprimaryColor]]; |
.)操作符,它可以提供方便的途径来调用一个对象的可访问方法。逗点操作符通常用在与声明的属性连接的特性,具体可以参考(“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-CRuntimeProgrammingGuide中的
消息的动态绑定方法与多态特性赋予了面向对象编程强大的可伸缩性。因为每个对象都有它们自己的方法,一条Objective-C语句可以得到各种结果,并非通过改变消息,而是靠改变接收消息的对象。***可以在程序运行时动态决定。***可以根据用户的动作等做出决定。
当执行基于ApplicationKit(AppKit)的代码时,例如,用户决定哪个对象需要从菜单命令中接收消息,如剪切,复制以及粘贴等。消息会找到当前正控制当前选项的对象。一个用来显示文本的对象与一个用来显示扫描图像的对象会对复制这个消息产生不同的反应。因为消息直到运行时才会选择所需的方法(换句话来讲,因为消息绑定的方法只有运行时才会执行),
这些行为上的不同让不同方法区别开来。发送消息的代码不用关注它们。它甚至不必猜测可能性。一个应用的对象都能以其队友的方式对复制这个消息做出回应。
Objective-C让动态绑定更进的一步,并且甚至允许被发送给变量的消息运行时才决定。至于如何实现,我们将在Objective-C
RuntimeProgrammingGuide中的
动态方法方案
你可以在运行时使用动态方法放来提供类和实例方法的实现。具体可以参考Objective-C
RuntimeProgrammingGuide的“Dynamic
MethodResolution”一节。
逗点语法
Objective-C提供了逗点(.)操作符,它可以为我们提供另外一种具有传统的方括号([])的功能的方式来访问方法。用法与C中类似
myInstance.value=10; |
printf("myInstancevalue:%d",myInstance.value); |
[myInstancesetValue:10]; |
printf("myInstancevalue:%d",[myInstancevalue]); |
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类型对象使用的严格限制.另外,对其它想要阅读你代码的人来说,它可以让你的意图更加清晰。但是,它并不能替代或者战胜动态绑定特性在运行时动态决定***的类的优点。
一个对象可以为它自己的类或者继承来的父类静态的赋予类型,例如,因为继承使
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:,
isMemberOfClass:,以及相关方法的更多信息NSObject
ClassReference
类对象
一个类的定义包含很多种信息,大多数是关于类的实例的:类名以及它的父类
用于描述一组实例变量的模板
方法名,返回类型,以及参数类型的声明
方法实现
这些信息将被编译并记录在数据结构中,并在运行时可用。编译器只会创建一个对象----一个类对象,代表这儿类。类对象可用访问有关这个类的所有信息,也就是说有关这个类的实例的是什么样的这类主要信息。类对象可用根据类中的定义产生新的实例。
尽管一个类对象持有一个类实例的协议,但它自身并不是一个实例。它自身也没有实例变量,但是,在类的定义可用包含为类对象使用的方法,类方法,有别于实例方法。一个类对象会从继承树中继承树中父类中的类方法。就像实例会继承实例方法一样。
在源代码中,类名代表着类对象。在下面的例子中
Rectangle类返回了该类的版本号,使用的是从
NSObject类中继承的方法:
intversionNumber=[Rectangleversion]; |
id.两者都返回
class消息:
idaClass=[anObjectclass]; |
idrectClass=[Rectangleclass]; |
Class类型。
ClassaClass=[anObjectclass]; |
ClassrectClass=[Rectangleclass]; |
Class.
未完还在整理中。。。
类对象是一种成熟完备的对象,可以动态的赋予类型,接收消息,并从其它类继承方法。它们的独特之处仅在于它们是有编译器创建的。缺乏它们自己的数据结构(实例变量)而非类的生命中构建的,运行时类变量还是用来生成实例的代理。
注意:编译器也会为每个类构建元类对象。元类对象能够描述类对象,就像类对象可以描述类的实例一样。但是当你给实例与类变量发送消息的时候,元类对象仅为运行时系统内部使用。
创建实例
类对象的一个主要功能就是穿件新的实例。下面的代码告诉Rectangle类去创建一个新的矩形实例,并将其赋给
myRectangle变量:
idmyRectangle; |
myRectangle=[Rectanglealloc]; |
myRectangle=[[Rectanglealloc]init]; |
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 |
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]]) |
... |
如果你在编译时不知道类名,而是在运行时作为一个字符串获取到,那么你可以使用
NSClassFromString
来返回类对象。
NSString*className; |
... |
if([anObjectisKindOfClass:NSClassFromString(className)]) |
... |
类名存在跟全局变量以及函数名存在于相同的命名空间。一个类跟全局变量不能重名。在Objective-C中类名是唯一具有全局可见的名字。
测试类是否相同
你可以直接使用指针来比较,测试两个类对象,看一下它们是否相同。很重要的一点是,要获取正确的类。在Cocoa框架中有几种特性,动态,透明的子类扩展了已经存在的类以扩展它们的功能。(例如KVO机制,以及CoreData框架就是这样的,可以参考Key-ValueObservingProgrammingGuide跟Core
DataProgrammingGuide)在一个动态创建的子类中,类方法通常会被重写,所以子类会装饰成被它取代的类。在测试两个类是否相等时,你需要比较类方法返回的值而非那些低级方法返回的值。按照API的条款,下面的不等式适用于动态子类。
[objectclass]!=object_getClass(object)!=*((Class*)object) |
if([objectAclass]==[objectBclass]){//... |
(
Objects,Classes,andMessaging
Thischapterdescribesthefundamentalsofobjects,classes,andmessagingasusedandimplementedbytheObjective-Clanguage.ItalsointroducestheObjective-Cruntime.
TheRuntimeSystem
TheObjective-Clanguagedefersasmanydecisionsasitcanfromcompiletimeandlinktimetoruntime.Wheneverpossible,itdynamicallyperformsoperationssuchascreatingobjectsanddeterminingwhatmethodtoinvoke.Therefore,thelanguagerequiresnotjustacompiler,butalsoaruntimesystemtoexecutethecompiledcode.TheruntimesystemactsasakindofoperatingsystemfortheObjective-Clanguage;
it’swhatmakesthelanguagework.Typically,however,youdon’tneedtointeractwiththeruntimedirectly.Tounderstandmoreaboutthefunctionalityitoffers,though,seeObjective-C
RuntimeProgrammingGuide.
Objects
Asthenameimplies,object-orientedprogramsarebuiltaroundobjects.Anobjectassociatesdatawiththeparticularoperationsthatcanuseoraffectthatdata.Objective-Cprovidesadatatypetoidentifyanobjectvariablewithoutspecifyingaparticularclassoftheobject.
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; |
idreplaces
intas
thedefaultdatatype.(ForstrictlyCconstructs,suchasfunctionreturnvalues,
intremainsthedefaulttype.)
Thekeyword
nilisdefined
asanullobject,an
idwithavalueof
0.
id,
nil,
andtheotherbasictypesofObjective-Caredefinedintheheaderfile
objc/objc.h.
idisdefinedaspointertoanobjectdatastructure:
typedefstructobjc_object{ |
Classisa; |
}*id; |
isavariablethattellsitofwhatclassitisaninstance.Sincethe
Classtype
isitselfdefinedasapointer:
typedefstructobjc_class*Class; |
isavariableisfrequentlyreferredtoasthe“
isapointer.”
DynamicTyping
Theidtypeiscompletelynonrestrictive.Byitself,ityieldsnoinformationaboutanobject,exceptthatitisanobject.
Atsomepoint,aprogramtypicallyneedstofindmorespecificinformationabouttheobjectsitcontains.Sincethe
idtypedesignatorcan’tsupplythisspecificinformation
tothecompiler,eachobjecthastobeabletosupplyitatruntime.
The
isainstancevariable
identifiestheobject’sclass—whatkindofobjectitis.Objectswiththesamebehavior(methods)andthesamekindsofdata(instancevariables)
aremembersofthesameclass.
Objectsarethusdynamicallytypedatruntime.Wheneveritneedsto,theruntimesystemcanfindtheexactclassthatanobjectbelongsto,justbyaskingtheobject.(Tolearnmoreaboutthe
runtime,seeObjective-CRuntime
ProgrammingGuide.)DynamictypinginObjective-Cservesasthefoundationfordynamicbinding,discussedlater.
The
isavariablealsoenablesobjectstoperformintrospection—to
findoutaboutthemselves(orotherobjects).Thecompilerrecordsinformationaboutclassdefinitionsindatastructuresfortheruntimesystemtouse.Thefunctionsoftheruntimesystemuse
isato
findthisinformationatruntime.Usingtheruntimesystem,youcan,forexample,determinewhetheranobjectimplementsaparticularmethodordiscoverthenameofitssuperclass.
Objectclassesarediscussedinmoredetailunder
It’salsopossibletogivethecompilerinformationabouttheclassofanobjectbystaticallytypingitinsourcecodeusingtheclassname.Classesareparticularkindsofobjects,andtheclassnamecanserve
asatypename.See“Class
Types”and“Enabling
StaticBehavior.”
MemoryManagement
Inanyprogram,itisimportanttoensurethatobjectsaredeallocatedwhentheyarenolongerneeded—otherwiseyourapplication’smemoryfootprintbecomeslargerthannecessary.Itisalsoimportanttoensurethatyoudonotdeallocateobjectswhilethey’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,andtheconceptsofpolymorphismanddynamicbinding.
MessageSyntax
Togetanobjecttodosomething,yousenditamessagetellingittoapplyamethod.InObjective-C,messageexpressionsare
enclosedinbrackets:
[receivermessage] |
issimplythenameofamethodandanyparametersthatarepassedtoit.Whenamessageissent,theruntimesystemselectstheappropriatemethodfromthe
receiver’srepertoireandinvokesit.
Forexample,thismessagetellsthe
myRectangleobjecttoperformits
displaymethod,
whichcausestherectangletodisplayitself:
[myRectangledisplay]; |
;”asisnormalforanystatementinC.
Becausethemethodnameinamessageservesto“select”amethodimplementation,methodnamesinmessagesareoftenreferredtoasselectors.
Methodscanalsotakeparameters,sometimescalledarguments.Amessagewithasingleparameteraffixes
acolon(
:)tothenameandputstheparameterrightafterthecolon:
[myRectanglesetWidth:20.0]; |
belowtellsthe
myRectangleobjecttosetitsorigintothecoordinates(30.0,50.0):
[myRectanglesetOriginX:30.0y:50.0];//Thisisagoodexampleof |
//multipleparameters |
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
theObjective-CRuntimeProgramming
Guide).Thus,thestructureofanObjective-CmethoddeclarationdiffersfromthestructureofamethodthatusesnamedorkeywordparametersinalanguagelikePython,asthefollowingPythonexampleillustrates:
deffunc(a,b,NeatMode=SuperNeat,Thing=DefaultThing): |
pass |
Thingand
NeatModemightbeomittedormighthavedifferentvalues
whencalled.
Inprinciple,a
Rectangleclasscouldinsteadimplementa
setOrigin::method
withnolabelforthesecondparameter,whichwouldbeinvokedasfollows:
[myRectanglesetOrigin:30.0:50.0];//Thisisabadexampleofmultipleparameters |
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]; |
isFilledto
YESif
myRectangleis
drawnasasolidrectangle,or
NOifit’sdrawninoutlineformonly.
BOOLisFilled; |
isFilled=[myRectangleisFilled]; |
Onemessageexpressioncanbenestedinsideanother.Here,thecolorofonerectangleissettothecolorofanother:
[myRectanglesetPrimaryColor:[otherRectprimaryColor]]; |
.)operatorthatoffersacompactandconvenientsyntaxforinvokinganobject’saccessormethods.
Thedotoperatorisoftenusedinconjunctionwiththedeclaredpropertiesfeature(see“Declared
Properties”)andisdescribedin“Dot
Syntax.”
SendingMessagestonil
InObjective-C,itisvalidtosendamessagetonil—itsimplyhasnoeffectatruntime.ThereareseveralpatternsinCocoa
thattakeadvantageofthisfact.Thevaluereturnedfromamessageto
nilmay
alsobevalid:
Ifthemethodreturnsanobject,thenamessagesentto
nilreturns
0(
nil).
Forexample:
Person*motherInLaw=[[aPersonspouse]mother]; |
spouseobjecthereis
nil,
then
motherissentto
nilandthemethodreturns
nil.
Ifthemethodreturnsanypointertype,anyintegerscalarofsizelessthanorequalto
sizeof(void*),
a
float,a
double,a
long,ora
double
longlong,thenamessagesentto
nilreturns
0.
Ifthemethodreturnsa
struct,asdefinedbytheMac
OSXABIFunctionCallGuidetobereturnedinregisters,thenamessagesentto
nilreturns
0.0for
everyfieldinthe
struct.Other
structdatatypeswillnotbefilledwithzeros.
Ifthemethodreturnsanythingotherthantheaforementionedvaluetypes,thereturnvalueofamessagesentto
nilis
undefined.
Thefollowingcodefragmentillustratesavaliduseofsendingamessageto
nil.
idanObjectMaybeNil=nil; |
//thisisvalid |
if([anObjectMaybeNilmethodThatReturnsADouble]==0.0) |
{ |
//implementationcontinues... |
} |
TheReceiver’sInstanceVariables
Amethodhasautomaticaccesstothereceivingobject’sinstancevariables.Youdon’tneedtopassthemtothemethodasparameters.Forexample,the
primaryColormethodillustratedabovetakesnoparameters,yetitcanfindtheprimarycolorfor
otherRectand
returnit.Everymethodassumesthereceiveranditsinstancevariables,withouthavingtodeclarethemasparameters.
ThisconventionsimplifiesObjective-Csourcecode.Italsosupportsthewayobject-orientedprogrammersthinkaboutobjectsandmessages.Messagesaresenttoreceiversmuchaslettersaredeliveredtoyour
home.Messageparametersbringinformationfromtheoutsidetothereceiver;theydon’tneedtobringthereceivertoitself.
Amethodhasautomaticaccessonlytothereceiver’sinstancevariables.Ifitrequiresinformationaboutavariablestoredinanotherobject,itmustsendamessagetotheobjectaskingittorevealthecontents
ofthevariable.The
primaryColorand
isFilledmethodsshownearlierareusedforjustthis
purpose.
See“Defining
aClass”formoreinformationonreferringtoinstancevariables.
Polymorphism
Astheearlierexamplesillustrate,messagesinObjective-CappearinthesamesyntacticpositionsasfunctioncallsinstandardC.But,becausemethods“belongto”anobject,messagesdon’tbehaveinthesamewaythatfunctioncallsdo.
Inparticular,anobjectcanbeoperatedonbyonlythosemethodsthatweredefinedforit.Itcan’tconfusethemwithmethodsdefinedforotherkindsofobject,evenifanotherobjecthasamethodwiththesame
name.Therefore,twoobjectscanresponddifferentlytothesamemessage.Forexample,eachkindofobjectthatreceivesa
displaymessagecoulddisplayitselfinaunique
way.A
Circleanda
Rectanglewouldresponddifferentlytoidenticalinstructionstotrack
thecursor.
Thisfeature,referredtoaspolymorphism,playsasignificantroleinthedesignofobject-oriented
programs.Togetherwithdynamicbinding,itpermitsyoutowritecodethatmightapplytoanynumberofdifferentkindsofobjects,withoutyouhavingtochooseatthetimeyouwritethecodewhatkindsofobjectstheymightbe.Theymightevenbeobjects
thatwillbedevelopedlater,byotherprogrammersworkingonotherprojects.Ifyouwritecodethatsendsa
displaymessagetoan
idvariable,
anyobjectthathasa
displaymethodisapotentialreceiver.
DynamicBinding
Acrucialdifferencebetweenfunctioncallsandmessagesisthatafunctionanditsparametersarejoinedtogetherinthecompiledcode,butamessageandareceivingobjectaren’tuniteduntiltheprogramisrunningandthemessageissent.Therefore,theexactmethodinvokedtorespondtoamessagecanbedeterminedonlyatruntime,notwhenthecodeiscompiled.
Whenamessageissent,aruntimemessagingroutinelooksatthereceiverandatthemethodnamedinthemessage.Itlocatesthereceiver’simplementationofamethodmatchingthename,“calls”themethod,and
passesitapointertothereceiver’sinstancevariables.(Formoreonthisroutine,see
RuntimeProgrammingGuide.)
Thisdynamicbindingofmethodstomessagesworkshandinhandwithpolymorphismtogiveobject-orientedprogrammingmuchofitsflexibilityandpower.Becauseeachobjectcanhaveitsownversion
ofamethod,anObjective-Cstatementcanachieveavarietyofresults,notbyvaryingthemessagebutbyvaryingtheobjectthatreceivesthemessage.Receiverscanbedecidedastheprogramruns;thechoiceofreceivercanbemadedependentonfactorssuch
asuseractions.
WhenexecutingcodebasedupontheApplicationKit(AppKit),forexample,usersdeterminewhichobjectsreceivemessagesfrommenucommandssuchasCut,Copy,andPaste.Themessagegoestowhatever
objectcontrolsthecurrentselection.Anobjectthatdisplaystextwouldreacttoa
copymessagedifferentlyfromanobjectthatdisplaysscannedimages.Anobjectthatrepresents
asetofshapeswouldresponddifferentlytoa
copymessagethana
Rectanglewould.Because
messagesdonotselectmethodsuntilruntime(fromanotherperspective,becausebindingofmethodstomessagesdoesnotoccuruntilruntime),thesedifferencesinbehaviorareisolatedtothemethodsthemselves.Thecodethatsendsthemessagedoesn’thave
tobeconcernedwiththem;itdoesn’tevenhavetoenumeratethepossibilities.Anapplication’sobjectscaneachrespondinitsownwayto
copymessages.
Objective-Ctakesdynamicbindingonestepfurtherandallowseventhemessagethat’ssent(themethodselector)tobeavariabledetermined
atruntime.Thismechanismisdiscussedinthesection
RuntimeProgrammingGuide.
DynamicMethodResolution
Youcanprovideimplementationsofclassandinstancemethodsatruntimeusingdynamicmethodresolution.See“DynamicMethodResolution”inObjective-C
RuntimeProgrammingGuideformoredetails.
DotSyntax
Objective-Cprovidesadot(.)operatorthatoffersanalternativetosquarebracketnotation(
[])
toinvokeaccessormethods.DotsyntaxusesthesamepatternthataccessingCstructureelementsuses:
myInstance.value=10; |
printf("myInstancevalue:%d",myInstance.value); |
Thecodeexampleaboveisexactlyequivalenttothefollowing:
[myInstancesetValue:10]; |
printf("myInstancevalue:%d",[myInstancevalue]); |
self,
forexample:
self.age=10; |
[selfsetAge:10]; |
self.,youaccesstheinstancevariabledirectly.Inthefollowingexample,thesetaccessormethodfor
ageisnotinvoked:
age=10; |
nilvalueisencounteredduringpropertytraversal,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.AprogrambasedontheCocoaframeworksmightuse
NSMatrixobjects,
NSWindowobjects,
NSDictionaryobjects,
NSFontobjects,
NSTextobjects,
andmanyothers.Programsoftenusemorethanoneobjectofthesamekindorclass—several
NSArrayobjectsor
NSWindowobjects,
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;eachnewclassthatyoudefineisbasedonanotherclassfromwhichitinheritsmethodsandinstancevariables.Thenewclasssimplyaddstoormodifieswhatitinherits.Itdoesn’tneedtoduplicateinheritedcode.
Inheritancelinksallclassestogetherinahierarchicaltreewithasingleclassatitsroot.
WhenwritingcodethatisbasedupontheFoundationframework,thatrootclassistypically
NSObject.
Everyclass(exceptarootclass)hasasuperclassone
stepnearertheroot,andanyclass(includingarootclass)canbethesuperclassforanynumberofsubclassesone
stepfartherfromtheroot.Figure1-1illustratesthehierarchyforafewoftheclassesusedinadrawingprogram.
Figure
1-1Somedrawingprogramclasses
Figure1-1showsthatthe
Squareclass
isasubclassofthe
Rectangleclass,the
Rectangleclassisasubclassof
Shape,
Shapeis
asubclassof
Graphic,and
Graphicisasubclassof
NSObject.
Inheritanceiscumulative.Soa
Squareobjecthasthemethodsandinstancevariablesdefinedfor
Rectangle,
Shape,
Graphic,
and
NSObject,aswellasthosedefinedspecificallyfor
Square.Thisissimplytosaythat
anobjectoftype
Squareisn’tonlyasquare,it’salsoarectangle,ashape,agraphic,andanobjectoftype
NSObject.
Everyclassbut
NSObjectcanthusbeseenasaspecializationor
anadaptationofanotherclass.Eachsuccessivesubclassfurthermodifiesthecumulativetotalofwhat’sinherited.The
Squareclass
definesonlytheminimumneededtoturnarectangleintoasquare.
Whenyoudefineaclass,youlinkittothehierarchybydeclaringitssuperclass;everyclassyoucreatemustbethesubclassofanotherclass(unlessyoudefineanewrootclass).Plentyofpotentialsuperclasses
areavailable.Cocoaincludesthe
NSObjectclassandseveralframeworkscontainingdefinitionsformorethan250additionalclasses.Someareclassesthatyoucanuseoffthe
shelfandincorporatethemintoyourprogramasis.Othersyoumightwanttoadapttoyourownneedsbydefiningasubclass.
Someframeworkclassesdefinealmosteverythingyouneed,butleavesomespecificstobeimplementedinasubclass.Youcanthuscreateverysophisticatedobjectsbywritingonlyasmallamountofcodeandreusing
workdonebytheprogrammersoftheframework.
TheNSObjectClass
NSObjectisarootclass,
andsodoesn’thaveasuperclass.ItdefinesthebasicframeworkforObjective-Cobjectsandobjectinteractions.Itimpartstotheclassesandinstancesofclassesthatinheritfromittheabilitytobehaveasobjectsandcooperatewiththeruntimesystem.
Aclassthatdoesn’tneedtoinheritanyspecialbehaviorfromanotherclassshouldneverthelessbemadeasubclassofthe
NSObjectclass.
InstancesoftheclassmustatleasthavetheabilitytobehavelikeObjective-Cobjectsatruntime.Inheritingthisabilityfromthe
NSObjectclassismuchsimplerandmuch
morereliablethanreinventingitinanewclassdefinition.
Note:Implementinganewrootclassisadelicatetaskandonewithmanyhiddenhazards.Theclassmustduplicatemuchofwhatthe
NSObjectclass
does,suchasallocateinstances,connectthemtotheirclass,andidentifythemtotheruntimesystem.Forthisreason,youshouldgenerallyusethe
NSObjectclassprovided
withCocoaastherootclass.Formoreinformation,seeNSObject
ClassReferenceandtheNSObject
ProtocolReference.
InheritingInstanceVariables
Whenaclassobjectcreatesanewinstance,thenewobjectcontainsnotonlytheinstancevariablesthatweredefinedforitsclassbutalsotheinstancevariablesdefinedforitssuperclassandforitssuperclass’ssuperclass,allthewaybacktotherootclass.Thus,the
isainstance
variabledefinedinthe
NSObjectclassbecomespartofeveryobject.
isaconnectseachobject
toitsclass.
Figure1-2showssomeoftheinstancevariablesthatcouldbedefinedforaparticular
implementationofa
Rectangleclassandwheretheymaycomefrom.Notethatthevariablesthatmaketheobjectarectangleareaddedtotheonesthatmakeitashape,andthe
onesthatmakeitashapeareaddedtotheonesthatmakeitagraphic,andsoon.
Figure
1-2Rectangleinstancevariables
Aclassdoesn’thavetodeclareinstancevariables.Itcansimplydefinenewmethodsandrelyontheinstance
variablesitinherits,ifitneedsanyinstancevariablesatall.Forexample,
Squaremightnotdeclareanynewinstancevariablesofitsown.
InheritingMethods
Anobjecthasaccessnotonlytothemethodsdefinedforitsclassbutalsotomethodsdefinedforitssuperclass,andforitssuperclass’ssuperclass,allthewaybacktotherootofthehierarchy.Forinstance,a
Squareobjectcanusemethods
definedinthe
Rectangle,
Shape,
Graphic,
and
NSObjectclassesaswellasmethodsdefinedinitsownclass.
Anynewclassyoudefineinyourprogramcanthereforemakeuseofthecodewrittenforalltheclassesaboveitinthehierarchy.Thistypeofinheritanceisamajorbenefitofobject-orientedprogramming.When
youuseoneoftheobject-orientedframeworksprovidedbyCocoa,yourprogramscantakeadvantageofthebasicfunctionalitycodedintotheframeworkclasses.Youhavetoaddonlythecodethatcustomizesthestandardfunctionalitytoyourapplication.
Classobjectsalsoinheritfromtheclassesabovetheminthehierarchy.Butbecausetheydon’thaveinstancevariables(onlyinstancesdo),theyinheritonlymethods.
OverridingOneMethodwithAnother
There’soneusefulexceptiontoinheritance:Whenyoudefineanewclass,youcanimplementanewmethodwiththesamenameasonedefinedinaclassfartherupthehierarchy.Thenewmethodoverridestheoriginal;instancesofthenewclassperformitratherthantheoriginal,andsubclassesofthenewclassinherititratherthantheoriginal.
Forexample,
Graphicdefinesa
displaymethod
that
Rectangleoverridesbydefiningitsownversionof
display.The
Graphicmethod
isavailabletoallkindsofobjectsthatinheritfromthe
Graphicclass—butnotto
Rectangleobjects,
whichinsteadperformthe
Rectangleversionof
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
Someclassesaredesignedonlyorprimarilysothatotherclassescaninheritfromthem.Theseabstractclassesgroupmethodsandinstancevariablesthatcanbeusedbyanumberofsubclassesintoacommondefinition.Theabstractclassistypically
incompletebyitself,butcontainsusefulcodethatreducestheimplementationburdenofitssubclasses.(Becauseabstractclassesmusthavesubclassestobeuseful,they’resometimesalsocalledabstractsuperclasses.)
Unlikesomeotherlanguages,Objective-Cdoesnothavesyntaxtomarkclassesasabstract,nordoesitpreventyoufromcreatinganinstanceofanabstractclass.
The
NSObjectclassisthecanonicalexampleofanabstractclassinCocoa.Youneveruseinstancesofthe
NSObjectclass
inanapplication—itwouldn’tbegoodforanything;itwouldbeagenericobjectwiththeabilitytodonothinginparticular.
The
NSViewclass,ontheotherhand,providesanexampleofanabstractclass,instancesofwhichyoumightoccasionallyuse
directly.
Abstractclassesoftencontaincodethathelpsdefinethestructureofanapplication.Whenyoucreatesubclassesoftheseclasses,instancesofyournewclassesfiteffortlesslyintotheapplicationstructure
andworkautomaticallywithotherobjects.
ClassTypes
Aclassdefinitionisaspecificationforakindofobject.Theclass,ineffect,definesadatatype.Thetypeisbasednotjustonthedatastructuretheclassdefines(instancevariables),butalsoonthebehaviorincludedinthedefinition(methods).
AclassnamecanappearinsourcecodewhereveratypespecifierispermittedinC—forexample,asanargumenttothe
sizeofoperator:
inti=sizeof(Rectangle); |
StaticTyping
Youcanuseaclassnameinplaceofidtodesignateanobject’stype:
Rectangle*myRectangle; |
Justas
idisactuallyapointer,objectsarestaticallytypedaspointers
toaclass.Objectsarealwaystypedbyapointer.Statictypingmakesthepointerexplicit;
idhidesit.
Statictypingpermitsthecompilertodosometypechecking—forexample,towarnifanobjectcouldreceiveamessagethatitappearsnottobeabletorespondto—andtoloosensomerestrictionsthatapplyto
objectsgenericallytyped
id.Inaddition,itcanmakeyourintentionsclearertootherswhoreadyoursourcecode.However,itdoesn’tdefeatdynamicbindingoralterthe
dynamicdeterminationofareceiver’sclassatruntime.
Anobjectcanbestaticallytypedtoitsownclassortoanyclassthatitinheritsfrom.Forexample,becauseinheritancemakesa
Rectangleobject
akindof
Graphicobject(asshownintheexamplehierarchyinFigure
1-1),a
Rectangleinstancecanbestaticallytypedtothe
Graphicclass:
Graphic*myRectangle; |
Rectangleobjectisa
Graphicobject.
Inaddition,it’smorethanthatbecauseitalsohastheinstancevariablesandmethodcapabilitiesof
Shapeand
Rectangleobjects,
butit’sa
Graphicobjectnonetheless.Forpurposesoftypechecking,giventhedeclarationdescribedhere,thecompilerconsiders
myRectangleto
beoftype
Graphic.Atruntime,however,ifthe
myRectangleobjectisallocatedandinitialized
asaninstanceof
Rectangle,itistreatedasone.
See“Enabling
StaticBehavior”formoreonstatictypinganditsbenefits.
TypeIntrospection
Instancescanrevealtheirtypesatruntime.TheisMemberOfClass:method,
definedinthe
NSObjectclass,checkswhetherthereceiverisaninstanceofaparticularclass:
if([anObjectisMemberOfClass:someClass]) |
... |
isKindOfClass:method,
alsodefinedinthe
NSObjectclass,checksmoregenerallywhetherthereceiverinheritsfromorisamemberofaparticularclass(whetherithastheclassinitsinheritance
path):
if([anObjectisKindOfClass:someClass]) |
... |
isKindOfClass:returns
YESis
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
Rectangleclassreturnsthe
classversionnumberusingamethodinheritedfromthe
NSObjectclass:
intversionNumber=[Rectangleversion]; |
id.
Bothrespondtoa
classmessage:
idaClass=[anObjectclass]; |
idrectClass=[Rectangleclass]; |
id.
Butclassobjectscanalsobemorespecificallytypedtothe
Classdatatype:
ClassaClass=[anObjectclass]; |
ClassrectClass=[Rectangleclass]; |
Class.Usingthistypenameforaclassisequivalenttousingtheclassnametostaticallytype
aninstance.
Classobjectsarethusfull-fledgedobjectsthatcanbedynamicallytyped,receivemessages,andinheritmethodsfromotherclasses.They’respecialonlyinthatthey’recreatedbythecompiler,lackdatastructures
(instancevariables)oftheirownotherthanthosebuiltfromtheclassdefinition,andaretheagentsforproducinginstancesatruntime.
Note:Thecompileralsobuildsametaclassobjectforeachclass.Itdescribestheclass
objectjustastheclassobjectdescribesinstancesoftheclass.Butwhileyoucansendmessagestoinstancesandtotheclassobject,themetaclassobjectisusedonlyinternallybytheruntimesystem.
CreatingInstances
Aprincipalfunctionofaclassobjectistocreatenewinstances.Thiscodetellsthe
Rectangleclasstocreateanewrectangleinstanceandassignittothe
myRectanglevariable:
idmyRectangle; |
myRectangle=[Rectanglealloc]; |
allocmethoddynamically
allocatesmemoryforthenewobject’sinstancevariablesandinitializesthemallto
0—all,thatis,exceptthe
isavariable
thatconnectsthenewinstancetoitsclass.Foranobjecttobeuseful,itgenerallyneedstobemorecompletelyinitialized.That’sthefunctionofan
initmethod.
Initializationtypicallyfollowsimmediatelyafterallocation:
myRectangle=[[Rectanglealloc]init]; |
myRectanglecouldreceiveanyofthemessagesthatwereillustrated
inpreviousexamplesinthischapter.The
allocmethodreturnsanewinstanceandthatinstanceperformsan
initmethod
tosetitsinitialstate.Everyclassobjecthasatleastonemethod(like
alloc)thatenablesittoproducenewobjects,andeveryinstancehasatleastonemethod(like
init)
thatpreparesitforuse.Initializationmethodsoftentakeparameterstoallowparticularvaluestobepassedandhavekeywordstolabeltheparameters(
initWithPosition:size:,
forexample,isamethodthatmightinitializeanew
Rectangleinstance),buteveryinitializationmethodbeginswith“
init”.
CustomizationwithClassObjects
It’snotjustawhimoftheObjective-Clanguagethatclassesaretreatedasobjects.It’sachoicethathasintended,andsometimessurprising,benefitsfordesign.It’spossible,forexample,tocustomizeanobjectwithaclass,wheretheclassbelongstoanopen-endedset.InAppKit,forexample,an
NSMatrixobject
canbecustomizedwithaparticularkindof
NSCellobject.
An
NSMatrixobjectcantakeresponsibilityforcreatingtheindividualobjectsthatrepresentitscells.Itcandothiswhen
thematrixisfirstinitializedandlaterwhennewcellsareneeded.Thevisiblematrixthatan
NSMatrixobjectdrawsonthescreencangrowandshrinkatruntime,perhaps
inresponsetouseractions.Whenitgrows,thematrixneedstobeabletoproducenewobjectstofillthenewslotsthatareadded.
Butwhatkindofobjectsshouldtheybe?Eachmatrixdisplaysjustonekindof
NSCell,buttherearemanydifferentkinds.The
inheritancehierarchyinFigure1-3showssomeofthoseprovidedbyAppKit.Allinheritfromthegeneric
NSCellclass.
Figure
1-3TheinheritancehierarchyforNSCell
Whenamatrixcreates
NSCellobjects,shouldtheybe
NSButtonCellobjects
todisplayabankofbuttonsorswitches,
NSTextFieldCellobjectstodisplayfieldswheretheusercanenterandedittext,orsomeotherkindof
NSCell?
The
NSMatrixobjectmustallowforanykindofcell,eventypesthathaven’tbeeninventedyet.
Onesolutiontothisproblemwouldbetodefinethe
NSMatrixclassasabstractandrequireeveryonewhousesittodeclarea
subclassandimplementthemethodsthatproducenewcells.Becausetheywouldbeimplementingthemethods,userscouldmakecertainthattheobjectstheycreatedwereoftherighttype.
Butthissolutionwouldrequireusersofthe
NSMatrixclasstodoworkthatoughttobedoneinthe
NSMatrixclass
itself,anditunnecessarilyproliferatesthenumberofclasses.Becauseanapplicationmightneedmorethanonekindofmatrix,eachwithadifferentkindofcell,itcouldbecomeclutteredwith
NSMatrixsubclasses.
Everytimeyouinventedanewkindof
NSCell,you’dalsohavetodefineanewkindof
NSMatrix.
Moreover,programmersondifferentprojectswouldbewritingvirtuallyidenticalcodetodothesamejob,alltomakeupforthefailureof
NSMatrixtodoit.
Abettersolution,andthesolutionthe
NSMatrixclassadopts,istoallow
NSMatrixinstances
tobeinitializedwithakindof
NSCell—thatis,withaclassobject.The
NSMatrixclass
alsodefinesa
setCellClass:methodthatpassestheclassobjectforthekindof
NSCellobject
an
NSMatrixshouldusetofillemptyslots:
[myMatrixsetCellClass:[NSButtonCellclass]]; |
NSMatrixobjectusestheclassobjecttoproducenewcellswhenit’sfirstinitializedandwheneverit’sresizedtocontain
morecells.Thiskindofcustomizationwouldbedifficultifclassesweren’tobjectsthatcouldbepassedinmessagesandassignedtovariables.
VariablesandClassObjects
Whenyoudefineanewclass,youcanspecifyinstancevariables.Everyinstanceoftheclasscanmaintainitsowncopyofthevariablesyoudeclare—eachobjectcontrolsitsowndata.Thereis,however,noclassvariablecounterparttoaninstancevariable.Onlyinternaldatastructures,initializedfromtheclassdefinition,areprovidedfortheclass.Moreover,a
classobjecthasnoaccesstotheinstancevariablesofanyinstances;itcan’tinitialize,read,oralterthem.
Foralltheinstancesofaclasstosharedata,youmustdefineanexternalvariableofsomesort.Thesimplestwaytodothisistodeclareavariableintheclassimplementationfile:
intMCLSGlobalVariable; |
@implementationMyClass |
//implementationcontinues |
static,andprovideclassmethodsto
manageit.Declaringavariable
staticlimitsitsscopetojusttheclass—andtojustthepartoftheclassthat’simplementedinthefile.(Thusunlikeinstancevariables,
staticvariablescannotbeinheritedby,ordirectlymanipulatedby,subclasses.)Thispatterniscommonlyusedtodefinesharedinstancesofaclass(suchassingletons;see“Creating
aSingletonInstance”inCocoa
FundamentalsGuide).
staticMyClass*MCLSSharedInstance; |
@implementationMyClass |
+(MyClass*)sharedInstance |
{ |
//checkforexistenceofsharedinstance |
//createifnecessary |
returnMCLSSharedInstance; |
} |
//implementationcontinues |
objectcanbeusedtocoordinatetheinstancesitcreates,dispenseinstancesfromlistsofobjectsalreadycreated,ormanageotherprocessesessentialtotheapplication.Inthecasewhenyouneedonlyoneobjectofaparticularclass,youcanputallthe
object’sstateintostaticvariablesanduseonlyclassmethods.Thissavesthestepofallocatingandinitializinganinstance.
Note:Itisalsopossibletouseexternalvariablesthatarenotdeclared
static,butthelimitedscopeof
staticvariablesbetterservesthepurposeofencapsulatingdataintoseparateobjects.
InitializingaClassObject
Ifyouwanttouseaclassobjectforanythingbesidesallocatinginstances,youmayneedtoinitializeitjustasyouwouldaninstance.Althoughprogramsdon’tallocateclassobjects,Objective-Cdoesprovideawayforprogramstoinitializethem.
Ifaclassmakesuseofstaticorglobalvariables,the
initializemethodisagoodplacetosettheirinitialvalues.Forexample,
ifaclassmaintainsanarrayofinstances,the
initializemethodcouldsetupthearrayandevenallocateoneortwodefaultinstancestohavethemready.
Theruntimesystemsendsan
initializemessage
toeveryclassobjectbeforetheclassreceivesanyothermessagesandafteritssuperclasshasreceivedthe
initializemessage.Thissequencegivestheclassachancetoset
upitsruntimeenvironmentbeforeit’sused.Ifnoinitializationisrequired,youdon’tneedtowritean
initializemethodtorespondtothemessage.
Becauseofinheritance,an
initializemessagesenttoaclassthatdoesn’timplementthe
initializemethod
isforwardedtothesuperclass,eventhoughthesuperclasshasalreadyreceivedthe
initializemessage.Forexample,assumeclassAimplementsthe
initializemethod,
andclassBinheritsfromclassAbutdoesnotimplementthe
initializemethod.JustbeforeclassBistoreceiveitsfirstmessage,theruntimesystemsends
initializeto
it.But,becauseclassBdoesn’timplement
initialize,classA’s
initializeisexecuted
instead.Therefore,classAshouldensurethatitsinitializationlogicisperformedonlyonce,andfortheappropriateclass.
Toavoidperforminginitializationlogicmorethanonce,usethetemplateinListing1-1when
implementingthe
initializemethod.
Listing1-1Implementationoftheinitializemethod
+(void)initialize |
{ |
if(self==[ThisClassclass]){ |
//Performinitializationhere. |
... |
} |
} |
initializetoeachclassindividually.Therefore,inaclass’s
implementationofthe
initializemethod,youmustnotsendthe
initializemessagetoits
superclass.
MethodsoftheRootClass
Allobjects,classesandinstancesalike,needaninterfacetotheruntimesystem.Bothclassobjectsandinstancesshouldbeabletointrospectabouttheirabilitiesandtoreporttheirplaceintheinheritancehierarchy.It’stheprovinceofthe
NSObjectclasstoprovidethisinterface.
Sothat
NSObjectmethodsdon’thavetobeimplementedtwice—oncetoprovidearuntimeinterfaceforinstancesandagaintoduplicate
thatinterfaceforclassobjects—classobjectsaregivenspecialdispensationtoperforminstancemethodsdefinedintherootclass.Whenaclassobjectreceives
amessagethatitcan’trespondtowithaclassmethod,theruntimesystemdetermineswhetherthere’sarootinstancemethodthat
canrespond.Theonlyinstancemethodsthataclassobjectcanperformarethosedefinedintherootclass,andonlyifthere’snoclassmethodthatcandothejob.
Formoreonthispeculiarabilityofclassobjectstoperformrootinstancemethods,seeNSObject
ClassReference.
ClassNamesinSourceCode
Insourcecode,classnamescanbeusedinonlytwoverydifferentcontexts.Thesecontextsreflectthedualroleofaclassasadatatypeandasanobject:
Theclassnamecanbeusedasatypenameforakindofobject.Forexample:
Rectangle*anObject; |
anObjectisstaticallytypedtobeapointertoa
Rectangleobject.
Thecompilerexpectsittohavethedatastructureofa
Rectangleinstanceandtohavetheinstancemethodsdefinedandinheritedbythe
Rectangleclass.
Statictypingenablesthecompilertodobettertypecheckingandmakessourcecodemoreself-documenting.See“Enabling
StaticBehavior”fordetails.
Onlyinstancescanbestaticallytyped;classobjectscan’tbe,becausetheyaren’tmembersofaclass,
butratherbelongtothe
Classdatatype.
Asthereceiverin
amessageexpression,theclassnamereferstotheclassobject.Thisusagewasillustratedinseveraloftheearlierexamples.Theclassnamecanstandfortheclassobjectonlyasamessagereceiver.Inanyothercontext,youmustasktheclassobjectto
revealits
id(bysendingita
classmessage).Thisexamplepassesthe
Rectangleclass
asaparameterinan
isKindOfClass:message:
if([anObjectisKindOfClass:[Rectangleclass]]) |
... |
Ifyoudon’tknowtheclassnameatcompiletimebuthaveitasastringatruntime,youcanuse
NSClassFromStringto
returntheclassobject:
NSString*className; |
... |
if([anObjectisKindOfClass:NSClassFromString(className)]) |
... |
nilifthestringit’spassedisnotavalidclassname.
Classnamesexistinthesamenamespaceasglobalvariablesandfunctionnames.Aclassandaglobalvariablecan’thavethesamename.ClassnamesaretheonlynameswithglobalvisibilityinObjective-C.
TestingClassEquality
Youcantesttwoclassobjectsforequalityusingadirectpointercomparison.Itisimportant,though,togetthecorrectclass.ThereareseveralfeaturesintheCocoaframeworksthatdynamicallyandtransparentlysubclassexistingclassestoextendtheirfunctionality(forexample,key-valueobservingandCoreDatadothis—seeKey-Value
ObservingProgrammingGuideandCore
DataProgrammingGuiderespectively).Inadynamically-createdsubclass,the
methodclass
istypicallyoverriddensuchthatthesubclassmasqueradesastheclassitreplaces.Whentestingforclassequality,youshouldthereforecomparethevaluesreturnedbythe
methodclass
ratherthanthosereturnedbylower-levelfunctions.PutintermsofAPI,thefollowinginequalitiespertainfordynamicsubclasses:
[objectclass]!=object_getClass(object)!=*((Class*)object) |
if([objectAclass]==[objectBclass]){//... |
相关文章推荐
- Objective-C 编程语言官网文档(六)-类别以及扩展
- Objective-c编程语言(二):对象,类,消息 ——对象
- Objective-c编程语言(三):对象,类,消息 ——消息
- Objective-C 编程语言(13)远程消息---分布式对象
- Objective-C 编程语言官网文档(七)-关联引用
- Objective-C 编程语言(2) 类,对象,消息 --- 类(4)类名称的用法
- Objective-C 编程语言官网文档(五)-属性的声明
- Objective-c编程语言(四):对象,类,消息 ——类
- Objective-C 编程语言官网文档(一)-简介
- Objective-C 编程语言官网文档(四)-协议
- Objective-C 编程语言官网文档(十一)-异常的处理
- Objective-C 编程语言(2) 类,对象,消息 --- 对象与消息
- Objective-C 编程语言官网文档(一)-简介
- Objective-C 编程语言官网文档(十一)-异常的处理
- Objective-C 编程语言官网文档(五)-属性的声明
- Objective-C 编程语言官网文档(八)-快速枚举
- Objective-C 编程语言官网文档(十三 终结篇)-词汇表
- Objective-C 编程语言(2) 类,对象,消息 --- 类(2)类类型
- Objective-C 编程语言官网文档(九)-静态行为
- Objective-C 编程语言官网文档(十三 终结篇)-词汇表