《Objective-C Runtime分析(三)-objc_msgSend》

本系列主要参考资料:

Objective-C Runtime Reference
Objective-C Runtime Programming Guide
涉及主要文件:objc/message.h,objc/objc-api.h,objc/objc.h,objc/runtime.h
特酷吧[tekuba.net]采用"署名-非商业用途-保持一致"的创作共用协议,使用本文内容请遵循该协议
Objective-C Runtime是Objective-C的基础内容,理解了Objective-C Runtime对于掌握Objective-C的很多技术原理非常有用。特酷吧特别整理了Objective-C Runtime的内容,共六篇,本文是第三篇:
Objective-C Runtime分析(一)-Runtime初步
Objective-C Runtime分析(二)-Class,Method,SEL,IMP
Objective-C Runtime分析(三)-objc_msgSend
Objective-C Runtime分析(四)--Dynamic Method Resolution
Objective-C Runtime分析(五)-Message Forwarding
Objective-C Runtime分析(六)-Type Encodings & Declared Properties

本部分主要参考:https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtHowMessagingWorks.html#//apple_ref/doc/uid/TP40008048-CH104-SW1
通过前两篇的学习,相信你已经对Objective-C Runtime有个一些了解,下面我们就该学习Objective-C Runtime中很重要的“消息”了。
一,obj_msgSend
情景:
Tekuba *tekuba = [[Tekuba alloc] init];
[tekuba url];
执行[tekuba url]时,编译器会自动的转换成objc_msgSend(tekuba, @selector(url));
objc_msgSend的原型是:
id objc_msgSend(id theReceiver, SELtheSelector, ...)
参数分别是消息接收对象,消息对应的方法的标识SEL,以及参数。
在执行objc_msgSend方法时,主要完成了以下几个工作:
The messaging function does everything necessary for dynamic binding:
(1),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.
它首先找到 SEL 对应的方法实现 IMP。因为不同的类对同一方法可能会有不同的实现,所以找到的方法实现依赖于消息接收者的类型。
(2),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.
然后将消息接收者对象(指向消息接收者对象的指针)以及方法中指定的参数传递给方法实现 IMP。
(3),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.

二,隐藏参数
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:
(1)The receiving object
(2)The selector for the method
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.
当objc_msgSend找到方法对应的实现时,它将直接调用该方法实现,并将消息中所有的参数都传递给方法实现,同时,它还将传递两个隐藏的参数:(1)消息的接收者;(2)方法名称 SEL。这些参数帮助方法实现获得了消息表达式的信息。它们被认为是"隐藏"的是因为它们并没有在定义方法的源代码中声明,而是在代码编译时是插入方法的实现中的。
尽管这些参数没有被显示声明,但在源代码中仍然可以引用它们。在方法中可以通过self来引用消息接收者对象,通过选标_cmd来引用方法本身。在下面的例子中,_cmd 指的是strange方法,self指的收到strange消息的对象。

折叠展开C/C++ Code复制内容到剪贴板

  1. - strange
  2. {
  3. id target = getTheReceiver();
  4. SEL method = getTheMethod();
  5. if (target == self || mothod == _cmd)
  6. return nil;
  7. return [target performSelector:method];
  8. }

在这两个参数中,self更有用一些。实际上,它是在方法实现中访问消息接收者对象的实例变量的途径。

三,获取方法地址
NSObject 类中的methodForSelector方法可以获取指向方法实现IMP的指针,methodForSelectorL返回的指针和赋值的变量类型必须完全一致,包括方法的参数类型和返回值类型。
下面的例子展示了怎么使用指针来调用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.
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.
Note that methodForSelector: is provided by the Cocoa runtime system; it‘s not a feature of the Objective-C language itself.
使用methodForSelector来避免动态绑定将减少大部分消息的开销,但是这只有在指定的消息被重复发送很多次时才有意义,例如上面的for循环。 [特酷吧的理解就是在基于NSObject的对象中,都有一个Class属性,维护了一个方法缓存,当多次调用的时候,是从缓存中找IMP,自然就比较快]。
注意,methodForSelector是Cocoa运行时系统的提供的功能,而不是Objective-C语言本身的功能。
转载请注明来自特酷吧,本文地址:www.tekuba.net/program/338/
推荐阅读:
IOS7运动视觉效果[UIInterpolatingMotionEffect]
IOS CoreAnimation核心动画(1)-CALayer和CAAnimation初步
iOS下TTS语音合成的几种方案
iOS 图片平铺和拉伸
iOS Quartz2D字体描边加粗等

想及时获取特酷吧的更新?想了解iOS,android开发最新技术动态,点击或扫描下方二维码下载“多识阅读”App,丰富的iOS,Android,Web等领域开发者博客随你订阅。

时间: 2024-11-05 18:42:32

《Objective-C Runtime分析(三)-objc_msgSend》的相关文章

CI框架源码阅读笔记3 全局函数Common.php

从本篇开始,将深入CI框架的内部,一步步去探索这个框架的实现.结构和设计. Common.php文件定义了一系列的全局函数(一般来说,全局函数具有最高的加载优先权,因此大多数的框架中BootStrap引导文件都会最先引入全局函数,以便于之后的处理工作). 打开Common.php中,第一行代码就非常诡异: if ( ! defined('BASEPATH')) exit('No direct script access allowed'); 上一篇(CI框架源码阅读笔记2 一切的入口 index

IOS测试框架之:athrun的InstrumentDriver源码阅读笔记

athrun的InstrumentDriver源码阅读笔记 作者:唯一 athrun是淘宝的开源测试项目,InstrumentDriver是ios端的实现,之前在公司项目中用过这个框架,没有深入了解,现在回来记录下. 官方介绍:http://code.taobao.org/p/athrun/wiki/instrumentDriver/ 优点:这个框架是对UIAutomation的java实现,在代码提示.用例维护方面比UIAutomation强多了,借junit4的光,我们可以通过junit4的

Yii源码阅读笔记 - 日志组件

?使用 Yii框架为开发者提供两个静态方法进行日志记录: Yii::log($message, $level, $category);Yii::trace($message, $category); 两者的区别在于后者依赖于应用开启调试模式,即定义常量YII_DEBUG: defined('YII_DEBUG') or define('YII_DEBUG', true); Yii::log方法的调用需要指定message的level和category.category是格式为“xxx.yyy.z

源码阅读笔记 - 1 MSVC2015中的std::sort

大约寒假开始的时候我就已经把std::sort的源码阅读完毕并理解其中的做法了,到了寒假结尾,姑且把它写出来 这是我的第一篇源码阅读笔记,以后会发更多的,包括算法和库实现,源码会按照我自己的代码风格格式化,去掉或者展开用于条件编译或者debug检查的宏,依重要程度重新排序函数,但是不会改变命名方式(虽然MSVC的STL命名实在是我不能接受的那种),对于代码块的解释会在代码块前(上面)用注释标明. template<class _RanIt, class _Diff, class _Pr> in

CI框架源码阅读笔记5 基准测试 BenchMark.php

上一篇博客(CI框架源码阅读笔记4 引导文件CodeIgniter.php)中,我们已经看到:CI中核心流程的核心功能都是由不同的组件来完成的.这些组件类似于一个一个单独的模块,不同的模块完成不同的功能,各模块之间可以相互调用,共同构成了CI的核心骨架. 从本篇开始,将进一步去分析各组件的实现细节,深入CI核心的黑盒内部(研究之后,其实就应该是白盒了,仅仅对于应用来说,它应该算是黑盒),从而更好的去认识.把握这个框架. 按照惯例,在开始之前,我们贴上CI中不完全的核心组件图: 由于BenchMa

CI框架源码阅读笔记2 一切的入口 index.php

上一节(CI框架源码阅读笔记1 - 环境准备.基本术语和框架流程)中,我们提到了CI框架的基本流程,这里这次贴出流程图,以备参考: 作为CI框架的入口文件,源码阅读,自然由此开始.在源码阅读的过程中,我们并不会逐行进行解释,而只解释核心的功能和实现. 1.       设置应用程序环境 define('ENVIRONMENT', 'development'); 这里的development可以是任何你喜欢的环境名称(比如dev,再如test),相对应的,你要在下面的switch case代码块中

Apache Storm源码阅读笔记

欢迎转载,转载请注明出处. 楔子 自从建了Spark交流的QQ群之后,热情加入的同学不少,大家不仅对Spark很热衷对于Storm也是充满好奇.大家都提到一个问题就是有关storm内部实现机理的资料比较少,理解起来非常费劲. 尽管自己也陆续对storm的源码走读发表了一些博文,当时写的时候比较匆忙,有时候衔接的不是太好,此番做了一些整理,主要是针对TridentTopology部分,修改过的内容采用pdf格式发布,方便打印. 文章中有些内容的理解得益于徐明明和fxjwind两位的指点,非常感谢.

CI框架源码阅读笔记4 引导文件CodeIgniter.php

到了这里,终于进入CI框架的核心了.既然是"引导"文件,那么就是对用户的请求.参数等做相应的导向,让用户请求和数据流按照正确的线路各就各位.例如,用户的请求url: http://you.host.com/usr/reg 经过引导文件,实际上会交给Application中的UsrController控制器的reg方法去处理. 这之中,CodeIgniter.php做了哪些工作?我们一步步来看. 1.    导入预定义常量.框架环境初始化 之前的一篇博客(CI框架源码阅读笔记2 一切的入

jdk源码阅读笔记之java集合框架(二)(ArrayList)

关于ArrayList的分析,会从且仅从其添加(add)与删除(remove)方法入手. ArrayList类定义: p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Monaco } span.s1 { color: #931a68 } public class ArrayList<E> extends AbstractList<E> implements List<E> ArrayList基本属性: /** *

dubbo源码阅读笔记--服务调用时序

上接dubbo源码阅读笔记--暴露服务时序,继续梳理服务调用时序,下图右面红线流程. 整理了调用时序图 分为3步,connect,decode,invoke. 连接 AllChannelHandler.connected(Channel) line: 38 HeartbeatHandler.connected(Channel) line: 47 MultiMessageHandler(AbstractChannelHandlerDelegate).connected(Channel) line: