Objective-c方法调用流程

  Objective-c是一门动态语言,动态两个字主要就体现在我们调用方法的时候,运行时回动态的查找方法,然后调用相应的函数地址。运行时是整个Objective-c程序的基石,有了它我们的程序才能正常运行起来。

  NSObject是Cocoa中绝大部分类的基类,它主要是提供了序列话,拷贝对象,以及支持运行时动态识别的框架。

  在Objective-c中每一个类对象最开始的位置都会有一个isa指针,该指针指向一块内存区域,该部分主要包含两部分信息:

  1、指向父类的指针。

  2、自身的方法分发表。

  有了这两部分,Objective-c的方法的调用流程就可以跑起来了。当我们调用一个对象的某一个方法的时候,首先会在当前类的分发表中寻找该方法,如果找不到对应的方法,然后再去其父类中寻找该方法,依次类推直到找到对应的方法为止,流程图如下:

  你可能会想到,如果一个类有很深的继承层次,每次去调用根类的某个函数,岂不是都要做很多次查找。理论上是这个样子的,不过runtime也并非那么傻,它会为每一个类(不是对象)维护一个经常调用的方法的列表,只要调用过就会缓存起来(官方没有明确说明缓存机制),这样当程序运行稳定以后整个方法调用的过程就会更加高效。

  通过学习官方文档Objective-C Runtime Programming Guide,可以发现其实所有的selector调用最后都会转化为C类型的函数调用。举个例子我们创建了一个A类型的对象aSample,然后调用其test方法([aSample test]),编译的时候,编译器就会将该调用转化为objc_send(aSample, selector)的形式,runtime会调用test方法实现所对应的函数地址。该函数的参数包含了两个隐含的参数self以及_cmd,其中self指向调用该方法的对象,_cmd则代表要调用的方法。

  前面提到了NSObject提供了很多遍历的方法可以和运行时进行交互,其中有个方法methodForSelector,通过它我们可以直接获取到指定的方法对应的函数指针。通常我们直接使用Objective-c方式的方法调用就可以了,但有时程序中可能会频繁的调用某一个方法,为了提高效率。我们可以直接获取到方法对应的函数地址,然后直接调用该函数,这样就少了动态识别的时间。

  下面举个例子:

// 父类中定义该方法
- (void)testMethod
{//NSLog(@"the implementation of BaseSample!!!");int a = 5 / 2.0f;a = ~a;
}// 测试方法,分别使用两种方法调用1亿次
- (void)test
{void (*methodAddress)(id,SEL);methodAddress = (void(*)(id,SEL))[self methodForSelector:@selector(testMethod)];NSLog(@"Invoke with Method Address start!!!");for (int i = 0; i < 100000000; ++i) {methodAddress(self, @selector(testMethod));}NSLog(@"Invoke with Method Address finish!!!");NSLog(@"Invoke with direct selector start!!!");for (int i = 0; i < 100000000; ++i) {[self testMethod];}NSLog(@"Invoke with direct selector finish!!!");
}

  运行结果如下图:

  可以看出调用时间:使用函数地址调用共花费0.151s,直接调用方法花费0.734s。时间是有一点儿差距,但是已经微乎其微了,这也从侧面说明了runtime的缓存机制还是很给力的。

  当我们调用某一个不存在的方法的时候,程序会crush,在命令行提示“unrecognized selector sent to instance 0xxxxxxx”,并抛出“NSInvalidArgumentException”的异常。当调用一个对象不能识别的方法时,runtime会一直沿着类的继承关系往基类方向寻找,直到NSObject类,如果还是识别不了该方法的话,再抛出异常之前runtime还给我们了最后一次“补救”的机会。它会先调用forwardInvocation方法,如果我们想把这个方法异常调用捕获并传递到其他地方的话,可以在类中重写该方法。NSObject对于forwardInvocation方法的默认实现是调用doesNotRecognizeSelector方法,而doesNotRecognizeSelector则是直接抛出异常。

  当调用forwardInvocation的时候会传入一个NSInvocation的参数,该参数标识了调用的方法的对象以及调用的方法,并对该方法的调用结果进行封装。我们重写forwardInvocation方法的时候,还必须同时重写methodSignatureForSelector方法,该方法返回表示一个方法的字符串,具体如何构建请看Type Encodings。

  下面举一个简单的重写forwardInvocation的例子:

#pragma mark-
#pragma mark 重写 ForwardInvocation- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{if ([self respondsToSelector:aSelector]) {return [super methodSignatureForSelector:aSelector];}else {return [NSMethodSignature signatureWithObjCTypes:"v@:"];}
}- (void)forwardInvocation:(NSInvocation *)anInvocation
{NSLog(@"Hello unreconginized selector!");
}// 在init中调用一个不存在的方法hello
- (id)initWithFrame:(CGRect)frame
{self = [super initWithFrame:frame];if (self) {// Initialization code
        [self hello];}return self;
}

  上面的例子,截获了不能识别的方法调用,创建了一个返回void类型的方法签名,当调用不能识别的方法的时候打印简单的日志。当然在程序中最好不要这么做,特别是开发的时候,大部分时候我们更希望能够尽早的发现这种调用错误。

  总结:Objective-c方法调用的流程就基本介绍完了,有什么写的不对的地方欢迎指正。

注:本文欢迎转载,转载请注明出处。同时欢迎加我qq,期待与你一起探讨更多问题。

转载于:https://www.cnblogs.com/smileEvday/archive/2012/11/26/messaging.html

Objective-c方法调用流程相关推荐

  1. import导包方法和路径问题和init方法调用流程

    import导包方法 共有三种方法哦~但不建议用"."因为如果两个包中都有同一个接口名,那么这时候进行"."导包就会使程序误解,到底要调用哪个呢,所以最好不要用 ...

  2. c语言访问oc变量,OC中的方法调用流程

    OC是一门动态语言,其方法调用方式与C++还是有很大区别的. 具体的方法调用过程,可以参考下面一片枫叶的博客,写的还是很详细的. 对于OC的方法调用,有两个点是重点: 1.对于OC的一切方法调用,最终 ...

  3. java 字节码查看_一种查看java字节码时显示方法调用关系图的方法与流程

    本发明涉及一种代码逻辑分析方法,具体涉及一种查看java字节码时显示方法调用关系图的方法. 背景技术: 目前软件反编译领域有不少对可执行文件进行反编译的工具如IDA,也有对Java代码生成的中间码文件 ...

  4. JAVA框架——struts(一)struts快速入门,struts访问流程,struts配置文件详解,动态方法调用

    一. Struts2框架概述 是一种基于MVC模式的轻量级web框架.本质是一个Servlet.作为控制器建立模型与视图的数据交互.Struts2以WebWord为核心,采用拦截器的机制处理客户的请求 ...

  5. html5动画怎么做成gif,一种网页版的调用html5视频录制动画GIF图像的方法与流程...

    本发明涉及WEB开发与应用技术领域,特别涉及一种网页版的调用html5视频录制动画GIF图像的方法. 背景技术: GIF 格式指的是图像交换格式(Graphics Interchange Format ...

  6. RPC 笔记(01)— RPC概念、调用流程、RPC 与 Restful API 区别

    1. 基本概念 PRC 远程过程调用 Remote Procedure Call,其就是一个节点请求另外一个节点提供的服务.当两个物理分离的子系统需要建立逻辑上的关联时,RPC 是牵线搭桥的常见技术手 ...

  7. novaclient的api调用流程与开发

    novaclient的api调用流程与开发 2015年07月05日 19:27:17 qiushanjushi 阅读数:3915 http://blog.csdn.net/tpiperatgod/ar ...

  8. c++ 重载 重写_Java | 深入理解方法调用的本质(含重载与重写区别)

    前言 对于习惯使用面向对象开发的工程师们来说,重载 & 重写 这两个概念应该不会陌生了.在中 / 低级别面试中,也常常会考察面试者对它们的理解(隐约记得当年在校招面试时遇到过): 网上大多数资 ...

  9. 【Android NDK 开发】JNI 方法解析 ( C/C++ 调用 Java 方法 | 函数签名 | 调用对象方法 | 调用静态方法 )

    文章目录 I . 调用 Java 方法流程 II . 获取 jclass 对象 ( GetObjectClass ) III . 获取 jclass 对象 ( FindClass ) IV . JNI ...

最新文章

  1. python中文读音ndarray-Python Numpy 控制台完全输出ndarray的实现
  2. sql数据库的基本操作
  3. # 睡眠3秒_【for fun】睡眠排序算法
  4. Java中的生成器设计模式
  5. pythonsearch结果_python 查询Elasticsearch的小例子
  6. 操作系统下代码设计与走读方法—业务线索法
  7. ECS 数据保护——数据备份新特性与最佳实践
  8. Lattice - 规划模块 1.采样轨迹 2.计算轨迹cost 3 循环检测筛选轨迹
  9. ORB feature to FAST,定向快速旋转简报
  10. appinfo.json
  11. 解读升压电路(BOOST)与降压电路(BUCK)
  12. badboy linux 版本,jmeter/Badboy安装教程
  13. 基于大数据技术推荐系统算法案例实战教程
  14. 4246. 【五校联考6day2】san (Standard IO)
  15. shields 徽标_纽约公共图书馆的新徽标
  16. GNS3 mac环境安装并搭建vlan
  17. Git_WorkFlow
  18. 12306登录python_python爬虫 -- 12306登录刷票
  19. 确定你到底喜欢什么事
  20. 吃货的痛点:鱼龙混杂,究竟我该相信谁

热门文章

  1. android 对java 支持_Android在未来对 Java 8 特性的支持
  2. 国内搭建vite vue和国外的不一样的,跟着教程会踩很多坑
  3. 计算机网络 故障处理,计算机网络通讯技术故障分析与处理
  4. matlab合成音乐原理,matlab 做音乐合成
  5. springboot配置文件加载顺序_「SpringBoot系列」配置文件加载优先级解析
  6. 购买域名以及申请证书
  7. 实验5 数组、指针与字符串
  8. ~~字符串哈希(数据结构)(附模板题AcWing 841 字符串哈希)
  9. matplotlib中改变字体的方法
  10. 网易云音乐随机歌曲调用API接口