iOS的消息转发机制

一、简介:

消息转发是OC底层一种功能强大的实现,为OC方法的调用增加更多的表现力和容错能力。什么是消息转发?简单来说,就是在OC在调用方法但不能找到方法对应实现时,执行的一种补救措施,从而将方法的执行引导到其他地方,为程序执行提供更多可能.

二、先来了解什么是消息发送

消息发送的官方定义:

屏幕快照 2019-01-25 上午11.24.37.png

官方文档

OC的方法本质:
OC方法的底层实现就是objc_msgSend()
objc_msgSend()前面两个参数:selfSEL。对self的理解一般认为它是对象本身,官方文档的解释是指向接收此消息的对象的指针,其实也不难理解,按照runtime的逻辑,方法的执行先要查找到本类的方法列表,然后执行,因此就需要知道本类是谁。对于_cmd(它保存了正在发送的消息的选择器)是第二个隐式参数,对应方法的实现。总之,self指向对象本身,_cmd指向方法本身。

OC中方法分为类方法和对象方法,对应的调用方式就是:
1.类名调用类方法:

// 对象实例调用
Person *person = [[Person alloc] init];
[person run];

2.对象实例调用实例方法:

// 类方法调用
[Person walk];

OC 函数调用的语法都会被翻译成一个 C 的函数调用 objc_msgSend()
我们用对象调用方法来举例子说明

1.先自定义一个Person类:


屏幕快照 2019-01-24 下午4.54.05.png

2.分别用person对象和消息发送来调用run方法:


屏幕快照 2019-01-24 下午4.53.10.png

屏幕快照 2019-01-24 下午5.09.46.png

3.查看打印结果:


屏幕快照 2019-01-24 下午4.53.27.png
屏幕快照 2019-01-24 下午5.10.08.png

4.可以看到打印了三次,说明方法被调用了三次.其中对象调用一次, objc_msgSend()调用了两次. objc_msgSend()就是方法调用的底层实现.

说明:
可以看到上面写了两种方式的objc_msgSend()调用,这是是LLVM的配置选项,可以选择关闭objc_msgSend的编写检查.具体操作如图:

屏幕快照 2019-01-24 下午5.08.20.png

5.消息发送的具体细节实现会另外写一篇文章.

二、消息转发...

当没有方法的实现,程序会在运行时挂掉并抛出 unrecognized selector sent to …的异常。但在异常抛出前,Objective-C 的运行时会给你三次拯救程序的机会:

  • 动态方法解析: Method Resolution
  • 快速转发: Fast Rorwarding
  • 完整消息转发: Normal Forwarding

系统在处理消息转发的时候,是按照上面的顺序进行转发的,转发成功则会跳过后面的方法.

1.动态方法解析: Method Resolution

首先,当调用没有实现的方法的时候,Objective-C 运行时会调用 + (BOOL)resolveInstanceMethod:或者 + (BOOL)resolveClassMethod:,让你有机会提供一个函数实现。如果你添加了函数并返回 YES, 那运行时系统就会重新启动一次消息发送的过程。

屏幕快照 2019-01-24 下午5.48.47.png

这里
v 代表函数返回类型void,
@ 代表self的类型id,
: 代表_cmd的类型SEL。

2.快速转发: Fast Rorwarding

  • Method Resolution 不同,Fast Rorwarding 这是一种快速消息转发:只需要在- (id)forwardingTargetForSelector:(SEL)aSelector 方法里面返回一个新对象即可。相当于替换了消息的接收者,进而去新的接收者那里去寻找对应的实现。
  • 通过- (id)forwardingTargetForSelector:(SEL)aSelector方法。如果此方法返回的是新的消息接收对象,则会向新对象转发此消息,如果此方法返回的是 nil 或者self,则会进入系统消息转发机制。具体为向 - (void)forwardInvocation:(NSInvocation *)invocation方法转发.
屏幕快照 2019-01-24 下午5.58.52.png

3. 完整消息转发: Normal Forwarding

与上面不同,可以理解成完整消息转发,用来代替快速转发做更多的事。

屏幕快照 2019-01-25 上午9.46.06.png

methodSignatureForSelector用来生成方法签名,这个签名就是给 forwardInvocation中的参数 NSInvocation调用的。通过NSInvocation中的SEL来执行下面的转发逻辑.

  • NSInvocation 的内部结构:


    屏幕快照 2019-01-25 上午10.18.15.png

1.methodSignatureForSelector这个方法中,如果没有找到方法对应的实现,就会返回一个空的方法签名,最终NSObject找不到SEL。系统就会报开头我们提到的 unrecognized selector sent to instance错误,最终导致程序报错崩溃。
2.所以我们需要做的是自己新建方法签名,再在forwardInvocation中用你要转发的那个对象调用这个对应的签名,这样也实现了消息转发。

屏幕快照 2019-01-25 上午10.24.23.png

上图中methodSignature为nil,导致-[NSObject(NSObject) doesNotRecognizeSelector:] 报错,引起程序崩溃.

三、总结

OC方法的调用通过消息发送的形式实现,当方法的实现找不到的情况下,运行时环境会依次进行下面三个阶段的查找:

第一阶段:
  • (BOOL)resolveInstanceMethod:(SEL)name(实例方法)
  • (BOOL)resolveClassMethod:(SEL)name(类方法)
第二阶段:
  • (id)forwardingTargetForSelector:(SEL)aSelector(快速转发)

在此方法中另外返回一个类的对象,该类含有对应方法的实现,runtime会在新类的方法列表中进行查找,找到就去执行,找不到依然会报错.

第三阶段:
  • (void)forwardInvocation:(NSInvocation *)invocation
  • (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector

methodSignatureForSelector中实现方法签名, forwardInvocation中根据methodSignatureForSelector返回的方法签名进行消息的转发.

参考链接:
https://www.jianshu.com/p/2fd4b930588e
https://www.jianshu.com/p/1bde36ad9938

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,678评论 0 9
  • 消息发送和转发流程可以概括为:消息发送(Messaging)是 Runtime 通过 selector 快速查找 ...
    lylaut阅读 1,815评论 2 3
  • 转载:http://yulingtianxia.com/blog/2014/11/05/objective-c-r...
    F麦子阅读 726评论 0 2
  • 关于OC中的消息发送的实现,在去年也看过一次,当时有点不太理解,但是今年再看却很容易理解。 我想这跟知识体系的构建...
    咖啡绿茶1991阅读 932评论 0 1
  • 说是急雨,不如说人心焦急 两点打雷,六点才下 雨,劈劈啪啪 铁棚被砸痛 匆忙的脚步躲不及 一道闪电照亮一角阴暗 刹...
    落花儿阅读 186评论 0 0