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

Objective-C 编程语言官网文档(九)-静态行为

2012-06-06 10:59 381 查看



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

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



我的编程环境:

IDE:XCODE4.3.1

OS:MACOSX10.7.4

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

静态行为

本节阐述了静态赋予类型是如何工作的,并讨论了Objective-C其它的一些特性,包括如何临时地停止它固有动态特性。


默认的动态行为

按照设计,Objective-C对象都是动态实体。关于它们的大多数决定从编译时推向运行时。

对象的内存是在运行时,类或者类方法创建新的实例时动态分配的。

对象是动态赋予类型的。在源码中(编译时),任何对象变量都可以是
id
类型的,无对象的类是什么。一个
id
变量(以及它的特定方法和数据结构)的实际所属类直到程序运行时才被确定。

消息和方法是动态绑定的,就像在“Dynamic
Binding.”中看到的。一个运行时进程会将消息中的方法选择器与“属于接收器”的方法实现进行匹配。

这些特性赋予了面向对象编程极大的可伸缩性以及能量,但也要付出一些代价。特别是编译器无法检查
id
变量的准确类型(类).要获得更好的编译时类型检查,并让代码有更好的可读性,Objective-C允许我们使用一个具体的类名来为变量静态赋予类型而不是泛泛的使用一个
id
类型。Objective-C
还允许我们关闭它的一些面向对象特性,以便将一些操作从运行时转到编译时来做。

注意:消息的方式比方法调用在某种程度上慢一些,但是这些性能消耗同实际执行的工作比起来微不足道。通过取消objc的灵活性而带来的性能变化只有通过分析工具诸如Shark或Instruments进行分析时才能感觉的到。


静态赋予类型

如果在一个对象声明的
id
处使用了一个指向一个类名的指针,例如

Rectangle*thisObject;

编译器会将声明的变量的值限制为声明中指定的类的一个实例或者从该类继承的子类的一个实例。在上面的例子中,
thisObject
只能是一个
Rectangle
类或其子类的对象。

静态赋予类型的对象跟声明为
id
类型的对象拥有相同的内部数据结构。类型并不会影响对象;它只会影响提供给编译器的有关该对象的信息量以及源码中有关对象的可读信息量。

静态赋予类型也不会影响对象在运行时的处理。为静态赋予类型的对象分配的类跟
id
类型创建实例的类是一样的。如果
Square
Rectangle
的子类,那么下面的代码也会创建一个拥有
Square
对象所有实例变量的对象,而不仅仅是
Rectangle
对象的实例变量:

Rectangle*thisObject=[[Squarealloc]init];

发送到静态类型对象的消息也是动态绑定的,同发送给
id
类型对象是相同的。静态指定类型的接受者在运行时的消息处理中任然会进行精确类型的检测。向
thisObject
对象发送
display
消息:

[thisObjectdisplay];

如上执行的是
Square
类中的display方法,而不是他的父类
Rectangle
中的方法。

作为提供给编译器额外的对象信息,静态类型比id类型提供了更多的可能:

在某些情况下,它可以进行编译时的类型校验。

它可以使对象摆脱相同名称的方法必须有相同参数类型和返回值的限制。

它允许你使用结构指针运算符(.运算符)直接访问一个对象的实例变量。

前两条会在下面章节讨论,第三条参见“Defining
aClass.”


类型检查

当使用静态类型时编译器可以在以下两种情况下提供更好的类型校验:

当向一个静态类型的接收者发送一个消息时,编译器可以确定接收者是否可以响应。如果接收者没有与消息中方法名相对应的方法编译器会发出一个警告。

当一个静态类型的对象被分配到一个静态类型的变量,编译器会判断他们的类型是否匹配。如果不匹配会发出一个警告。

若要赋值操作时避免警告,需要对象的类型同变量的类型相同或者为变量类型的子类。就像下面的例子所展示的:

Shape*aShape;

Rectangle*aRect;


aRect=[[Rectanglealloc]init];

aShape=aRect;

这里
aRect
可以被分配给
aShape
,因为矩形是图形的一种——
Rectangle
类继承自Shape。但是,如果两者的角色调换一下也就是说将
aShape
分配给
aRect
,编译器会生成一个警告,因为不是所有的图形都是矩形。(可以参见1-2,
该处展示了类的继承关系,其中包括
Shape
Rectangle
.)

当表达式中等号两边的任何一个对象的类型为
id
时都不会进行类型校验。静态类型的对象可以任意的分配给
id
类型的对象,或者
id
类型的对象也可以分配给任何一个静态类型对象。由于像
alloc
init
这样的方法会返回
id
类型的对象,所以编译器并不能确保为静态类型变量返回一个类型合适的对象。像下面这样为变量赋值虽然很容易出错误,但是仍然是被允许的:

Rectangle*aRect;

aRect=[[Shapealloc]init];


返回类型和参数类型

通常情况下,不同类中的同名方法其参数和返回值类型也必须相同。这一限制由编译器强制执行以实现动态绑定。因为在运行时编译器无法知道一个消息接收者的类型(实现被调用方法的类),编译器必须对相同名字的方法相似对待。当编译器为运行时系统准备方法的信息时,它仅创建一个方法描述来应对每一个方法选择器。

但是,当向静态类型对象发送一个消息时,编译器知道接收者的类型。编译器可以取得关于这个特定类中方法的定义信息,因此消息的返回值和参数类型就不再受上面的约束。


为一个继承类赋予静态类型

一个类的实例可以被静态的制定为它本身类的类型也可以被指定为它所继承的类的类型。例如,所有的实例都可以被静态的指定为
NSObject
类型。

但是,编译器只是通过类型定义中的类名来确定类的静态类型并进行类型校验。因此为一个实例指定一个它所继承类的类型会使编译器所预期的处理同运行时实际的处理出现偏差。

例如,如果你把一个
Rectangle
实例指定为
Shape
类型:

Shape*myRectangle=[[Rectanglealloc]init];

编译器会认为它就是一个
Shape
实例。如果你向这个对象发送一个消息让其运行一个
Rectangle
中的方法:

BOOLsolid=[myRectangleisFilled];

编译器会表示很愤怒

。因为
isFilled
方法是在
Rectangle
类中定义的而不是在
Shape
中。

但是,如果发送一个消息,来运行一个
Shape
中的方法:

[myRectangledisplay];

编译器表示很淡定

,即使
Rectangle
已经覆写了这个方法。但是在运行时,实际运行的方法是
Rectangle
版本中定义的方法。

类似,假如有一个
Upper
类声明了一个
worry
方法,它有一个
double
类型返回值:

-(double)worry;

同时
Upper
的子类
Middle
复写了这个方法并返回了一个新类型的返回值:

-(int)worry;

如果一个实例被静态的指定为
Upper
类型,编译器会认为他的
worry
方法返回一个
double
类型返回值,同时如果一个实例被指定为
Middle
类型,编译器会认为
worry
应该返回一个int类型。如果一个Middle实例被指定为
Upper
类型编译器会通知运行时系统对象的
worry
方法返回一个
double
类型,但是在运行时实际的返回值为
int
类型同时会生成一个错误。

静态赋予类型可以使同名方法无须受到必须具有相同参数类型及返回值类型的约束,但是只有应用在不同继承关系中定义的方法,这样做才可靠。

英文原文:点击打开链接


EnablingStaticBehavior

ThischapterexplainshowstatictypingworksanddiscussessomeotherfeaturesofObjective-C,includingwaystotemporarilyovercomeitsinherentdynamism.


DefaultDynamicBehavior

Bydesign,Objective-Cobjectsaredynamicentities.Asmanydecisionsaboutthemaspossiblearepushedfromcompiletimetoruntime:

Thememoryforobjectsisdynamicallyallocatedatruntime
byclassmethodsthatcreatenewinstances.

Objectsaredynamicallytyped.Insourcecode(atcompiletime),anyobjectvariablecanbeoftype
id
no
matterwhattheobject’sclassis.Theexactclassofan
id
variable(andthereforeitsparticularmethodsanddatastructure)isn’tdetermineduntiltheprogramruns.

Messagesandmethodsaredynamicallybound,asdescribedin“Dynamic
Binding.”Aruntimeprocedurematchesthemethodselectorinthemessagetoamethodimplementationthat“belongsto”thereceiver.

Thesefeaturesgiveobject-orientedprogramsagreatdealofflexibilityandpower,butthere’sapricetopay.Inparticular,thecompilercan’tchecktheexacttypes(classes)of
id
variables.
Topermitbettercompile-timetypechecking,andtomakecodemoreself-documenting,Objective-Callowsobjectstobestaticallytypedwithaclass
nameratherthangenericallytypedas
id
.Objective-Calsoletsyouturnoffsomeofitsobject-orientedfeaturesinordertoshiftoperationsfromruntimebacktocompile
time.

Note:Messagesaresomewhatslowerthanfunctioncalls,typicallyincurringaninsignificantamountofoverheadcomparedtoactualworkperformed.Theexceptionallyrarecasewherebypassing
thedynamismofObjective-CmightbewarrantedcanbeprovenbyuseofanalysistoolslikeSharkorInstruments.


StaticTyping

Ifapointertoaclassnameisusedinplaceof
id
in
anobjectdeclarationsuchas

Rectangle*thisObject;

thecompilerrestrictsthevalueofthedeclaredvariabletobeeitheraninstanceoftheclassnamedinthedeclarationoraninstanceofaclassthatinheritsfromthenamedclass.Intheexampleabove,
thisObject
can
beonlya
Rectangle
objectofsomekind.

Staticallytypedobjectshavethesameinternaldatastructuresasobjectsdeclaredtobeoftype
id
.
Thetypedoesn’taffecttheobject;itaffectsonlytheamountofinformationgiventothecompilerabouttheobjectandtheamountofinformationavailabletothosereadingthesourcecode.

Statictypingalsodoesn’taffecthowtheobjectistreatedatruntime.Staticallytypedobjectsaredynamicallyallocatedbythesameclassmethodsthatcreateinstancesoftype
id
.
If
Square
isasubclassof
Rectangle
,thefollowingcodewouldstillproduceanobjectwith
alltheinstancevariablesofa
Square
object,notjustthoseofa
Rectangle
object:

Rectangle*thisObject=[[Squarealloc]init];

Messagessent
tostaticallytypedobjectsaredynamicallybound,justasmessagestoobjectstyped
id
are.Theexacttypeofastaticallytypedreceiverisstilldeterminedatruntimeas
partofthemessagingprocess.A
display
messagesenttothe
thisObject
object:

[thisObjectdisplay];

performstheversionofthemethoddefinedinthe
Square
class,nottheoneinits
Rectangle
superclass.

Bygivingthecompilermoreinformationaboutanobject,statictypingopensuppossibilitiesthatareabsentforobjectstyped
id
:

Incertainsituations,itallowsforcompile-timetypechecking.

Itcanfreeobjectsfromtherestrictionthatidenticallynamedmethodsmusthaveidenticalreturnandparametertypes.

Itpermitsyoutousethestructurepointeroperatortodirectlyaccessanobject’sinstancevariables.

Thefirsttwopossibilitiesarediscussedinthesectionsthatfollow.Thethirdiscoveredin“Defining
aClass.”


TypeChecking

Withtheadditionalinformationprovidedbystatictyping,thecompilercandeliverbettertype-checkingservicesintwosituations:

Whenamessageissenttoastaticallytypedreceiver,thecompilercanmakesurethereceivercanrespond.Awarningisissuedifthereceiverdoesn’thaveaccesstothemethodnamedinthe
message.

Whenastaticallytypedobjectisassignedtoastaticallytypedvariable,thecompilermakessurethetypesarecompatible.Awarningisissuedifthey’renot.

Anassignmentcanbemadewithoutwarning,providedtheclassoftheobjectbeingassignedisidenticalto,orinheritsfrom,theclassofthevariablereceivingtheassignment.Thefollowingexampleillustrates
this:

Shape*aShape;

Rectangle*aRect;


aRect=[[Rectanglealloc]init];

aShape=aRect;

Here
aRect
canbeassignedto
aShape
because
arectangleisakindofshape—the
Rectangle
classinheritsfrom
Shape
.However,iftheroles
ofthetwovariablesarereversedand
aShape
isassignedto
aRect
,thecompilergenerates
awarning;noteveryshapeisarectangle.(Forreference,seeFigure
1-2,whichshowstheclasshierarchyincluding
Shape
and
Rectangle
.)

There’snocheckwhentheexpressiononeithersideoftheassignmentoperatorisoftype
id
.Astaticallytypedobjectcan
befreelyassignedtoan
id
object,oran
id
objecttoastaticallytypedobject.Because
methodslike
alloc
and
init
returnobjectsoftype
id
,
thecompilerdoesn’tensurethatacompatibleobjectisreturnedtoastaticallytypedvariable.Thefollowingcodeiserror-prone,butisallowednonetheless:

Rectangle*aRect;

aRect=[[Shapealloc]init];


ReturnandParameterTypes

Ingeneral,methodsin
differentclassesthathavethesameselector(thesamename)mustalsosharethesamereturnandparametertypes.Thisconstraintisimposedby
thecompilertoallowdynamicbinding.Becausetheclassofamessagereceiver(andthereforeclass-specificdetailsaboutthemethodit’sasked
toperform),can’tbeknownatcompiletime,thecompilermusttreatallmethodswiththesamenamealike.Whenitpreparesinformationonmethodreturnandparametertypesfor
theruntimesystem,itcreatesjustonemethoddescriptionforeachmethodselector.

However,whenamessageissenttoastaticallytypedobject,theclassofthereceiverisknownbythecompiler.Thecompilerhasaccesstoclass-specificinformationaboutthemethods.Therefore,themessage
isfreedfromtherestrictionsonitsreturnandparametertypes.


StaticTypingtoanInheritedClass

Aninstancecanbestaticallytypedtoitsownclassortoanyclassthatitinheritsfrom.Allinstances,forexample,canbestaticallytypedas
NSObject
.

However,thecompilerunderstandstheclassofastaticallytypedobjectonlyfromtheclassnameinthetypedesignation,anditdoesitstypecheckingaccordingly.Typinganinstancetoaninheritedclasscan
thereforeresultindiscrepanciesbetweenwhatthecompilerthinkswouldhappenatruntimeandwhatactuallyhappens.

Forexample,ifyoustaticallytypea
Rectangle
instanceas
Shape
as
shownhere:

Shape*myRectangle=[[Rectanglealloc]init];

thecompilertreatsitasa
Shape
instance.Ifyousendtheobjectamessagetoperforma
Rectangle
method,

BOOLsolid=[myRectangleisFilled];

thecompilercomplains.The
isFilled
methodisdefinedinthe
Rectangle
class,
notin
Shape
.

However,ifyousenditamessagetoperformamethodthatthe
Shape
classknowsaboutsuchas

[myRectangledisplay];

thecompilerdoesn’tcomplain,eventhough
Rectangle
overridesthemethod.Atruntime,the
Rectangle
version
ofthemethodisperformed.

Similarly,supposethatthe
Upper
classdeclaresa
worry
method
thatreturnsa
double
asshownhere:

-(double)worry;

andthe
Middle
subclassof
Upper
overrides
themethodanddeclaresanewreturntype:

-(int)worry;

Ifaninstanceisstaticallytypedtothe
Upper
class,thecompilerthinksthatits
worry
method
returnsa
double
,andifaninstanceistypedtothe
Middle
class,thecompilerthinksthat
worry
returns
an
int
.Errorsresultifa
Middle
instanceistypedtothe
Upper
class:
Thecompilerinformstheruntimesystemthata
worry
messagesenttotheobjectreturnsa
double
,
butatruntimeitactuallyreturnsan
int
andgeneratesanerror.

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