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

[译]Objective-C Runtime Programming Guide -Messaging (二)

2014-11-21 17:09 471 查看




Messaging

发送消息

Thischapterdescribeshowthemessageexpressionsareconvertedinto
objc_msgSend
function
calls,andhowyoucanrefertomethodsbyname.Itthenexplainshowyoucantakeadvantageof
objc_msgSend
,andhow—ifyouneedto—youcancircumvent
dynamicbinding.

本章介绍如何在消息表达式转换成objc_msgSend函数调用,以及如何通过名称来引用的方法。然后,它解释了如何利用objc_msgSend的优势,以及如何绕过动态绑定(如果你需要的话)。

Theobjc_msgSendFunction

InObjective-C,messagesaren’tboundtomethodimplementationsuntilruntime.Thecompilerconvertsamessageexpression,

[receivermessage]

在Objective-C中,消息不一定要绑定实现方法直到运行的时候。编译器会进行消息表达式的转换。

intoacallonamessagingfunction,
objc_msgSend
.
Thisfunctiontakesthereceiverandthenameofthemethodmentionedinthemessage—thatis,themethodselector—asitstwo
principalparameters:

objc_msgSend(receiver,selector)

call一个消息函数,objc_msgSend这个函数拿着接收者和方法的名字在消息中被提及,就是说这个方法被选择了,就像这两个主要的参数:

objc_msgSend(receiver,selector)

Anyargumentspassedinthemessagearealsohandedto
objc_msgSend
:

objc_msgSend(receiver,selector,arg1,arg2,...)

Themessagingfunctiondoeseverythingnecessaryfordynamicbinding:

Itfirstfindstheprocedure(methodimplementation)thattheselectorrefers
to.Sincethesamemethodcanbeimplementeddifferentlybyseparateclasses,thepreciseprocedurethatitfindsdependsontheclassofthereceiver.

Itthencallstheprocedure,passingitthereceivingobject(apointertoitsdata),alongwithanyargumentsthatwerespecifiedforthemethod.

Finally,itpassesonthereturnvalueoftheprocedureasitsownreturnvalue.

Note:Thecompilergeneratescallstothemessagingfunction.Youshouldnevercallitdirectlyinthecodeyouwrite.

任何参数被交给消息都用objc_msgSend

objc_msgSend(receiver,selector,arg1,arg2,...)

这消息函数做一切必要的动态绑定:

它首先找到程序(方法的实现)所指的,因为同样的方法可以有不同的实现by单独的类,这个确切的过程取决于它找到的发送者的类。

然后它调用程序,通过他来接收对象(一个指向他的数据指针),以及用于该方法中指定的任何参数。

最后,通过程序的值返回值作为其自身的返回值。

备注:编译器生成调用消息的函数。你不需要在你写的代码直接调用它。

Thekeytomessagingliesinthestructuresthatthecompilerbuildsforeachclassandobject.Everyclassstructureincludesthesetwoessentialelements:

Apointertothesuperclass.

Aclassdispatchtable.
Thistablehasentriesthatassociatemethodselectorswiththeclass-specificaddressesofthemethodstheyidentify.Theselectorforthe
setOrigin::
method
isassociatedwiththeaddressof(theprocedurethatimplements)
setOrigin::
,theselectorforthe
display
method
isassociatedwith
display
’saddress,andsoon.

对于Messageing来说关键的结构在于编译器对于每一个类和对象的编译。每个类的结构包括两个基本元素:

一个指向父类的指针。

一个类的调度表。这个表里有识别方法类的特定地址用来联系相关联的方法选择器的条目。选择器setOrigin::方法进行地址关联(一个实现过程)setOrigin::这个选择器用于显示方法来联系地址,依次类推。

Whenanewobjectiscreated,memoryforitisallocated,anditsinstancevariablesareinitialized.Firstamong
theobject’svariablesisapointertoitsclassstructure.Thispointer,called
isa
,
givestheobjectaccesstoitsclassand,throughtheclass,toalltheclassesitinheritsfrom.

当创建一个对象的时候,内存被分配,其实例变量被初始化。第一个对象的变量是一个指向类结构的指针。这个指针被称作isa,给这个目标访问类,经过这个类给所有从他这边继承的类。

Note:While
notstrictlyapartofthelanguage,the
isa
pointer
isrequiredforanobjecttoworkwiththeObjective-Cruntimesystem.Anobjectneedstobe“equivalent”toa
structobjc_object
(defined
in
objc/objc.h
)inwhatever
fieldsthestructuredefines.However,yourarely,ifever,needtocreateyourownrootobject,andobjectsthatinheritfrom
NSObject
or
NSProxy
automatically
havethe
isa
variable.

注意:虽然语言不是严格意义上的一部分,isa指针需要一个在Objective-C运行时系统工作的对象。一个对象必须是“等价于”一个structobjc_object(定义在objc/objc.h)在任何字段的结构定义。然而,你很少这样,如果有的话,需要创建你自己的根对象,并这些对象继承于NSObject
或NSProxy并自动的拥有isa变量。


These
elementsofclassandobjectstructureareillustratedinFigure
3-1.



Whenamessageissenttoanobject,themessagingfunctionfollowstheobject’s
isa
pointer
totheclassstructurewhereitlooksupthemethodselectorinthedispatchtable.Ifitcan’tfindtheselectorthere,
objc_msgSend
follows
thepointertothesuperclassandtriestofindtheselectorinitsdispatchtable.Successivefailurescause
objc_msgSend
toclimbtheclass
hierarchyuntilitreachesthe
NSObject
class.Onceitlocatestheselector,thefunctioncallsthemethodenteredinthetableandpassesit
thereceivingobject’sdatastructure.

Thisisthewaythatmethodimplementationsarechosenatruntime—or,inthejargonofobject-orientedprogramming,thatmethodsaredynamicallyboundtomessages.

Tospeedthemessagingprocess,theruntimesystemcachestheselectorsandaddressesofmethodsastheyareused.There’saseparatecacheforeachclass,anditcancontainselectorsforinheritedmethodsaswellasformethodsdefinedintheclass.Before
searchingthedispatchtables,themessagingroutinefirstchecksthecacheofthereceivingobject’sclass(onthetheorythatamethodthatwasusedoncemaylikelybeusedagain).Ifthemethodselectorisinthecache,messagingisonlyslightlyslower
thanafunctioncall.Onceaprogramhasbeenrunninglongenoughto“warmup”itscaches,almostallthemessagesitsendsfindacachedmethod.Cachesgrowdynamicallytoaccommodatenewmessagesastheprogramruns.

当一个消息被发送到一个对象,在它查找在调度表中的方法选择器的时候,该消息传递函数跟着对象的isa指针指向类的结构。如果在这里它不能找到选择器,objc_msgSend跟着这个指针到父类中去并试着找到选择器。连续的查找失败导致objc_msgSend
向上跨越等级(继承的等级)直到到达NSObject类。一旦找到了选择器,这个函数调用在表中的方法and传递给接受对象的数据结构。


这是一种选择在运行时实现这个方法,或者说这个是面向对象编程的中术语的方法,该方法动态绑定到这个消息。

为了加快消息传递的过程,运行时系统缓存了选择器和他们使用的地址。这些都是对每个类独立的缓存,并且它可以包含选择器继承的方法以及用于在该类中定义的方法。在搜索调度表之前,消息收发程序首先检查该接收对象的类的高速缓存(在理论上可能有可能被再次使用的,使用后一方法)。如果该方法选择器是在高速缓存中,消息发送只会比一个函数调用稍微慢一点。一旦程序已经运行足够长的时间来“热身”,它的高速缓存,几乎所有发送的消息都会找到一个缓存的方法。因为程序在运行,高速缓存会动态增长以适应新的消息。


UsingHiddenArguments

When
objc_msgSend
finds
theprocedurethatimplementsamethod,itcallstheprocedureandpassesitalltheargumentsinthemessage.Italsopassestheproceduretwohiddenarguments:

Thereceivingobject

Theselectorforthemethod

Theseargumentsgiveeverymethodimplementationexplicitinformationaboutthetwohalvesofthemessageexpressionthatinvokedit.They’resaidtobe“hidden”becausetheyaren’tdeclaredinthesourcecodethatdefinesthemethod.They’reinsertedinto
theimplementationwhenthecodeiscompiled.

使用隐藏的参数

当objc_msgSend找到实现的方法,它在消息中调用程序并传递参数。它在过程中还传递两个隐藏参数:

1、接收的对象

2、选择器的方法

这些参数给每一个方法的实现提供关于这两部分的消息表达式的明显的信息。他们说是“隐藏的”,因为它们不是在源代码中定义的方法声明。他们在编译时被插入到执行代码中。

Althoughtheseargumentsaren’texplicitlydeclared,sourcecodecanstillrefertothem(justasitcanrefertothereceivingobject’sinstancevariables).Amethodreferstothereceivingobjectas
self
,
andtoitsownselectoras
_cmd
.
Intheexamplebelow,
_cmd
referstotheselectorforthe
strange
method
and
self
totheobjectthatreceivesa
strange
message.

-strange

{

idtarget=getTheReceiver();

SELmethod=getTheMethod();


if(target==self||method==_cmd)

returnnil;

return[targetperformSelector:method];

}

self
isthemoreusefulofthetwoarguments.Itis,infact,thewaythereceivingobject’sinstancevariablesaremadeavailabletothemethod
definition.

虽然这些参数没有被显式地声明,源代码仍然可以访问他们(正如它可以指到接收对象的实例变量)。一种方法使用接收的对象就像使用self一样,他拥有的选择器作为_cmd。在下面的例子中,_cmd是用于陌生方法的参考选择器且对于方法的自身是一个模式的消息。

(代码其实我也没怎么看懂)。。求指导~

self的两个参数比较有用,事实上接收对象的实例变量是被提供方法定义的方式。


GettingaMethodAddress

Theonlywaytocircumventdynamicbindingistogettheaddressofamethodand
callitdirectlyasifitwereafunction.Thismightbeappropriateontherareoccasionswhenaparticularmethodwillbeperformedmanytimesinsuccessionandyouwanttoavoidtheoverheadofmessagingeachtimethemethodisperformed.
Withamethoddefinedinthe
NSObject
class,
methodForSelector:
,
youcanaskforapointertotheprocedurethatimplementsamethod,thenusethepointertocalltheprocedure.Thepointerthat
methodForSelector:
returnsmustbecarefullycasttotheproperfunctiontype.
Bothreturnandargumenttypesshouldbeincludedinthecast.

规避动态结合的唯一方法是让一个方法的地址,并直接调用它,就好像它是一个函数。这可能是适当的在极少数情况下,当一个特定的方法将被连续的执行多次,你想避免这个方法每次执行的开销。

随着NSObject类,methodForSelector中定义的方法:,你可以要求一个指向实现方法的程序,然后使用指针来调用该程序。这个methodForSelector:指针必须返回一个安全正确的函数类型。返回的参数类型必须包括在描述中。

Theexamplebelowshowshowtheprocedurethatimplementsthe
setFilled:
methodmightbecalled:

void(*setter)(id,SEL,BOOL);

inti;


setter=(void(*)(id,SEL,BOOL))[target

methodForSelector:@selector(setFilled:)];

for(i=0;i<1000;i++)

setter(targetList[i],@selector(setFilled:),YES);

Thefirsttwoargumentspassedtotheprocedurearethereceivingobject(
self
)
andthemethodselector(
_cmd
).
Theseargumentsarehiddeninmethodsyntaxbutmustbemadeexplicitwhenthemethodiscalledasafunction.
Using
methodForSelector:
tocircumventdynamicbindingsavesmostofthetimerequiredbymessaging.
However,thesavingswillbesignificantonlywhereaparticularmessageisrepeatedmanytimes,asinthe
for
loopshownabove.
Notethat
methodForSelector:
isprovidedbytheCocoaruntimesystem;it’snotafeatureoftheObjective-C
languageitself.

传递给该程序的前两个参数是接收中的对象(self)和方法的选择(_cmd)。这些参数都隐藏在方法的语法中,但该方法被函数调用时必须明确。

使用methodForSelector:用消息绕过动态绑定以节省所需的大部分时间。然而,这个节省只有在一个特定的消息被重复了很多次的时候是显著的,比如如上图所示在for循环。

需要注意的是methodForSelector:由Cocoa运行时系统提供的;这不是Objective-C语言本身的特点。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: