iOS学习笔记

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

2019-01-15  本文已影响0人  郝嗨森

官方文档

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.

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

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.

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

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语言本身的特性。

上一篇下一篇

猜你喜欢

热点阅读