3.Runtime官方文档学习--消息

官方文档

This chapter describes how the message expressions are converted into objc_msgSend function calls, and how you can refer to methods by name. It then explains how you can take advantage of objc_msgSend, and how—if you need to—you can circumvent dynamic binding.

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

1.objc_msgSend函数

在Objective-C中,消息直到运行时才被绑定到方法实现上。如下的消息表达式

[receiver message]

编译器会把它转化成消息函数 objc_msgSend的调用。这个函数把接收者和消息中提到的方法名称(即方法选择器)作为它的两个主要参数:

objc_msgSend(receiver, selector)

消息中传递的任何参数也会传递给objc_msgSend

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

The messaging function does everything necessary for dynamic binding:

  • It first finds the procedure (method implementation) that the selector refers to. Since the same method can be implemented differently by separate classes, the precise procedure that it finds depends on the class of the receiver.
  • It then calls the procedure, passing it the receiving object (a pointer to its data), along with any arguments that were specified for the method.
  • Finally, it passes on the return value of the procedure as its own return value.

消息函数可以完成动态绑定所需的任何事情。

  • 它首先找到选择器引用的程序(方法实现)。由于相同的方法在不同的类中可以有不同的实现,因此找到准确的程序取决于receiver所在类。
  • 然后它调用程序,把接收对象(指向数据的指针)和方法指定的任意参数传递给它。
  • 最后,它将程序的返回值作为自己的返回值继续传递。

Note: The compiler generates calls to the messaging function. You should never call it directly in the code you write.
注意:编译器会生成对消息函数的调用。永远不要在写的代码中直接调用它。

The key to messaging lies in the structures that the compiler builds for each class and object. Every class structure includes these two essential elements:

  • A pointer to the superclass.
  • A class dispatch table. This table has entries that associate method selectors with the class-specific addresses of the methods they identify. The selector for the setOrigin:: method is associated with the address of (the procedure that implements) setOrigin::, the selector for the display method is associated with display’s address, and so on.

消息的关键在于编译器为每个类和对象构建的结构。每个类的结构都包括以下两个基本要素:

  • 指向父类的指针
  • 调度表。这个表具有将方法选择器与指定类中标识的方法的地址相关联的条目。方法setOrigin::的选择器和setOrigin::的地址(实现程序)相关联,display方法的选择器与display的地址相关联,以此类推。

When a new object is created, memory for it is allocated, and its instance variables are initialized. First among the object’s variables is a pointer to its class structure. This pointer, called isa, gives the object access to its class and, through the class, to all the classes it inherits from.

当创建一个新的对象时,会为其分配内存,其实例变量会被初始化。对象第一个变量是指向其类结构的指针。这个叫做isa的指针让这个对象可以访问它的类,以及通过这个类访问所有继承的类。

Note: While not strictly a part of the language, the isa pointer is required for an object to work with the Objective-C runtime system. An object needs to be “equivalent” to a struct objc_object (defined in objc/objc.h) in whatever fields the structure defines. However, you rarely, if ever, need to create your own root object, and objects that inherit from NSObject or NSProxy automatically have the isa variable.
注意:虽然严格来说不属于语言的一部分,但是isa指针是对象在Objective-C运行时系统中工作所必需的。在结构定义的任何区域对象都需要和结构体objc_object(定义在objc/objc.h)“等效”。然而,你很少(如果有)需要去创建你自己的根对象,并且继承自NSObjectNSProxy的对象会自动拥有isa变量。

类和对象结构的这些元素如下图所示。


消息框架

When a message is sent to an object, the messaging function follows the object’s isa pointer to the class structure where it looks up the method selector in the dispatch table. If it can’t find the selector there, objc_msgSend follows the pointer to the superclass and tries to find the selector in its dispatch table. Successive failures cause objc_msgSend to climb the class hierarchy until it reaches the NSObject class. Once it locates the selector, the function calls the method entered in the table and passes it the receiving object’s data structure.

当一条消息发送给一个对象,消息函数会从对象的指向在调度表中查找方法选择器的类结构的isa指针开始查找。如果找不到选择器,objc_msgSend尝试循着指向父类的指针并在其调度表中找到选择器。
连续的失败会导致objc_msgSend攀爬类的层次结构直到到达NSObject类。一旦找到选择器,函数会调用表中的方法并把接收的对象的数据结构传递给它。

This is the way that method implementations are chosen at runtime—or, in the jargon of object-oriented programming, that methods are dynamically bound to messages.

这就是在运行时选择方法实现的方式--或者在面向对象编程的术语中,方法被动态地绑定到消息。

To speed the messaging process, the runtime system caches the selectors and addresses of methods as they are used. There’s a separate cache for each class, and it can contain selectors for inherited methods as well as for methods defined in the class. Before searching the dispatch tables, the messaging routine first checks the cache of the receiving object’s class (on the theory that a method that was used once may likely be used again). If the method selector is in the cache, messaging is only slightly slower than a function call. Once a program has been running long enough to “warm up” its caches, almost all the messages it sends find a cached method. Caches grow dynamically to accommodate new messages as the program runs.

为了加速消息传递,运行时系统在使用方法的选择器和地址时会进行缓存。每个类都有一个单独的缓存,他可以包含继承方法的选择器以及类中定义的方法。在查找调度表之前,消息事物首先会检测接收者对象类的缓存(理论上可能会再次被使用的方法)。如果方法选择器在缓存中,消息只比函数调用慢一点点。一旦程序运行了足够长的时间来“预热”其缓存,它发送的几乎所有消息都会找到一个缓存的方法。随着程序运行缓存会动态增长以容纳新的消息。

2.使用隐藏的参数

When objc_msgSend finds the procedure that implements a method, it calls the procedure and passes it all the arguments in the message. It also passes the procedure two hidden arguments:

  • The receiving object
  • The selector for the method

objc_msgSend找到实现了一个方法的程序时,会调用这个程序并把消息中的所有参数传递给它。它也会把两个隐藏的参数传递给程序:

  • 接收对象
  • 方法选择器

These arguments give every method implementation explicit information about the two halves of the message expression that invoked it. They’re said to be “hidden” because they aren’t declared in the source code that defines the method. They’re inserted into the implementation when the code is compiled.

这些参数为每个方法实现提供关于调用它的消息表达式的两半部分的显示的信息。它们被称为“隐藏的”,因为它们没有在定义方法的源代码中被声明。而是在代码编译时被插入到实现中。

Although these arguments aren’t explicitly declared, source code can still refer to them (just as it can refer to the receiving object’s instance variables). A method refers to the receiving object as self, and to its own selector as _cmd. In the example below, _cmd refers to the selector for the strange method and self to the object that receives a strange message.

尽管这些参数没有被显示地声明,源代码依然可以引用它们(就像它引用接收者对象的实例变量)。方法引用接收者作为self,将它自己的选择器作为_cmd。在下面的例子中,_cmd指向strange方法的选择器,self指向接收strange消息的对象。

- strange
{
    id  target = getTheReceiver();
    SEL method = getTheMethod();
 
    if ( target == self || method == _cmd )
        return nil;
    return [target performSelector:method];
}

self is the more useful of the two arguments. It is, in fact, the way the receiving object’s instance variables are made available to the method definition.

self是这两个参数中更有用的。事实上,它是接收者对象的实例变量可用于方法定义的方式。(不太理解)

3.获取方法地址

The only way to circumvent dynamic binding is to get the address of a method and call it directly as if it were a function. This might be appropriate on the rare occasions when a particular method will be performed many times in succession and you want to avoid the overhead of messaging each time the method is performed.

规避动态绑定的唯一方法是获取方法的地址然后直接调用它,就好像它是一个函数一样。这可能只适用于极少数情况下----当一个特定的方法将被连续多次执行并且你想要在每次方法执行时避免消息的开销。

With a method defined in the NSObject class, methodForSelector:, you can ask for a pointer to the procedure that implements a method, then use the pointer to call the procedure. The pointer that methodForSelector: returns must be carefully cast to the proper function type. Both return and argument types should be included in the cast.

使用NSObject类中定义的方法methodForSelector:,你可以请求得到指向实现方法的程序的指针,然后使用这个指针来调用程序。methodForSelector:返回的指针必须小心地转换成正确的函数类型。返回值和参数类型都应包含在转换中。

The example below shows how the procedure that implements the setFilled: method might be called:
下面的例子展示了实现了setFilled:方法的程序应该如何被调用:

void (*setter)(id, SEL, BOOL);
int i;
 
setter = (void (*)(id, SEL, BOOL))[target
    methodForSelector:@selector(setFilled:)];
for ( i = 0 ; i < 1000 ; i++ )
    setter(targetList[i], @selector(setFilled:), YES);

The first two arguments passed to the procedure are the receiving object (self) and the method selector (_cmd). These arguments are hidden in method syntax but must be made explicit when the method is called as a function.

开始传递给程序的两个参数是接收者对象(self)和方法选择器(_cmd)。这些参数在方法语法中是隐藏的,但是当方法作为函数被调用时必须显示出来。

Using methodForSelector: to circumvent dynamic binding saves most of the time required by messaging. However, the savings will be significant only where a particular message is repeated many times, as in the for loop shown above.

使用methodForSelector:来规避动态绑定节省了消息传递需要的大部分的时间。然而,只有在特定的消息重复很多次时节省才有意义,如上面所示的for循环。

Note that methodForSelector: is provided by the Cocoa runtime system; it’s not a feature of the Objective-C language itself.

注意methodForSelector:由Cocoa运行时系统提供,它不是Objective-C语言本身的特性。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • The Inner Game of Tennis W Timothy Gallwey Jonathan Cape ...
    网事_79a3阅读 13,069评论 3 20
  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi阅读 7,906评论 0 10
  • 昨夜刷《爆裂鼓手》到凌晨两点睡。 关于这个电影我还是想看第二遍以后再记录。震撼和触动,但是想沉淀一下。 可能是抱怨...
    July77阅读 167评论 0 0
  • 教育观察NO.40‖,教育是一种因感动而教化,也是一种因教化而感动的事业。在教育的过程中,如果先能感动孩子,再予以...
    边根元阅读 241评论 0 0
  • 画的这个小象挂饰看起来前腿有些长。
    宛茹阅读 230评论 0 3

友情链接更多精彩内容