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,编译器会生成一个警告,因为不是所有的图形都是矩形。(可以参见
该处展示了类的继承关系,其中包括
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
idno
matterwhattheobject’sclassis.Theexactclassofan
idvariable(andthereforeitsparticularmethodsanddatastructure)isn’tdetermineduntiltheprogramruns.
Messagesandmethodsaredynamicallybound,asdescribedin“Dynamic
Binding.”Aruntimeprocedurematchesthemethodselectorinthemessagetoamethodimplementationthat“belongsto”thereceiver.
Thesefeaturesgiveobject-orientedprogramsagreatdealofflexibilityandpower,butthere’sapricetopay.Inparticular,thecompilercan’tchecktheexacttypes(classes)of
idvariables.
Topermitbettercompile-timetypechecking,andtomakecodemoreself-documenting,Objective-Callowsobjectstobestaticallytypedwithaclass
nameratherthangenericallytypedas
id.Objective-Calsoletsyouturnoffsomeofitsobject-orientedfeaturesinordertoshiftoperationsfromruntimebacktocompile
time.
Note:Messagesaresomewhatslowerthanfunctioncalls,typicallyincurringaninsignificantamountofoverheadcomparedtoactualworkperformed.Theexceptionallyrarecasewherebypassing
thedynamismofObjective-CmightbewarrantedcanbeprovenbyuseofanalysistoolslikeSharkorInstruments.
StaticTyping
Ifapointertoaclassnameisusedinplaceofidin
anobjectdeclarationsuchas
Rectangle*thisObject; |
thisObjectcan
beonlya
Rectangleobjectofsomekind.
Staticallytypedobjectshavethesameinternaldatastructuresasobjectsdeclaredtobeoftype
id.
Thetypedoesn’taffecttheobject;itaffectsonlytheamountofinformationgiventothecompilerabouttheobjectandtheamountofinformationavailabletothosereadingthesourcecode.
Statictypingalsodoesn’taffecthowtheobjectistreatedatruntime.Staticallytypedobjectsaredynamicallyallocatedbythesameclassmethodsthatcreateinstancesoftype
id.
If
Squareisasubclassof
Rectangle,thefollowingcodewouldstillproduceanobjectwith
alltheinstancevariablesofa
Squareobject,notjustthoseofa
Rectangleobject:
Rectangle*thisObject=[[Squarealloc]init]; |
tostaticallytypedobjectsaredynamicallybound,justasmessagestoobjectstyped
idare.Theexacttypeofastaticallytypedreceiverisstilldeterminedatruntimeas
partofthemessagingprocess.A
displaymessagesenttothe
thisObjectobject:
[thisObjectdisplay]; |
Squareclass,nottheoneinits
Rectanglesuperclass.
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; |
aRectcanbeassignedto
aShapebecause
arectangleisakindofshape—the
Rectangleclassinheritsfrom
Shape.However,iftheroles
ofthetwovariablesarereversedand
aShapeisassignedto
aRect,thecompilergenerates
awarning;noteveryshapeisarectangle.(Forreference,seeFigure
1-2,whichshowstheclasshierarchyincluding
Shapeand
Rectangle.)
There’snocheckwhentheexpressiononeithersideoftheassignmentoperatorisoftype
id.Astaticallytypedobjectcan
befreelyassignedtoan
idobject,oran
idobjecttoastaticallytypedobject.Because
methodslike
allocand
initreturnobjectsoftype
id,
thecompilerdoesn’tensurethatacompatibleobjectisreturnedtoastaticallytypedvariable.Thefollowingcodeiserror-prone,butisallowednonetheless:
Rectangle*aRect; |
aRect=[[Shapealloc]init]; |
ReturnandParameterTypes
Ingeneral,methodsindifferentclassesthathavethesameselector(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,canbestaticallytypedasNSObject.
However,thecompilerunderstandstheclassofastaticallytypedobjectonlyfromtheclassnameinthetypedesignation,anditdoesitstypecheckingaccordingly.Typinganinstancetoaninheritedclasscan
thereforeresultindiscrepanciesbetweenwhatthecompilerthinkswouldhappenatruntimeandwhatactuallyhappens.
Forexample,ifyoustaticallytypea
Rectangleinstanceas
Shapeas
shownhere:
Shape*myRectangle=[[Rectanglealloc]init]; |
Shapeinstance.Ifyousendtheobjectamessagetoperforma
Rectanglemethod,
BOOLsolid=[myRectangleisFilled]; |
isFilledmethodisdefinedinthe
Rectangleclass,
notin
Shape.
However,ifyousenditamessagetoperformamethodthatthe
Shapeclassknowsaboutsuchas
[myRectangledisplay]; |
Rectangleoverridesthemethod.Atruntime,the
Rectangleversion
ofthemethodisperformed.
Similarly,supposethatthe
Upperclassdeclaresa
worrymethod
thatreturnsa
doubleasshownhere:
-(double)worry; |
Middlesubclassof
Upperoverrides
themethodanddeclaresanewreturntype:
-(int)worry; |
Upperclass,thecompilerthinksthatits
worrymethod
returnsa
double,andifaninstanceistypedtothe
Middleclass,thecompilerthinksthat
worryreturns
an
int.Errorsresultifa
Middleinstanceistypedtothe
Upperclass:
Thecompilerinformstheruntimesystemthata
worrymessagesenttotheobjectreturnsa
double,
butatruntimeitactuallyreturnsan
intandgeneratesanerror.
Statictypingcanfreeidenticallynamedmethodsfromtherestrictionthattheymusthaveidenticalreturnandparametertypes,butitcandosoreliablyonlyifthemethodsaredeclaredindifferentbranches
oftheclasshierarchy.
相关文章推荐
- Objective-C 编程语言官网文档(九)-静态行为
- Objective-C 编程语言官网文档(二)-对象,类以及消息
- Objective-C 编程语言官网文档(十)-选择器
- Objective-C 编程语言官网文档(十)-选择器
- Objective-C 编程语言官网文档(七)-关联引用
- Objective-C 编程语言官网文档(八)-快速枚举
- Objective-C 编程语言官网文档(五)-属性的声明
- Objective-C 编程语言官网文档(五)-属性的声明
- Objective-C 编程语言官网文档(六)-类别以及扩展
- Objective-C 编程语言(9)使能静态行为----类型检查、返回值和参数的类型、将派生类静态地转换为基类类型
- Objective-C 编程语言官网文档(一)-简介
- Objective-C 编程语言官网文档(三)-如何定义类
- Objective-C 编程语言(9)使能静态行为----缺省的动态行为、静态类型
- Objective-C 编程语言官网文档(一)-简介
- Objective-C 编程语言官网文档(三)-如何定义类
- Objective-C 编程语言官网文档(十一)-异常的处理
- Objective-C 编程语言官网文档(十一)-异常的处理
- Objective-C 编程语言官网文档(五)-属性的声明
- Objective-C 编程语言官网文档(四)-协议
- Objective-C 编程语言官网文档(十二)-线程