我眼中的runtime(消息转发)

runtime可以帮助我们实现一些oc层的api达不到的功能。那就先需要了解一下。

一、消息转发

oc中的动态特性,就是他在运行的时候,才能确定某些东西。比如实现方法这个过程。实际上是一个发送消息的过程。这个消息,也许是由接受者执行,也可能是由开发者设定的其他对象执行。消息与方法的绑定,也是运行时才确定.

消息的发送过程是这样的:
1.通过对象的isa指针找到它的类
2.在类的method list 中找到这个方法(根据SEL来找,方法的唯一辨识)
3.如果class中没有这个方法,就沿着这个类的isa指针 指向它的superclass去找
4.一旦找到,则执行这个方法的IMP(函数指针,指向方法执行的首地址)

但是,如果每次都要这么找的话,没有必要每次都在methodlist中循环查找,所以,一旦找到了这个方法,就存为class_cache,下一次就先在这里找,所以这一步应在执行在上文步骤1的前面。


如果找不到,而开发者没有做任何处理。那么自然会出现 unrecognized selector sent to instance的错误
其实在这个异常出现之前,会有三个步骤来转发这个消息,如果我们不做任何一个动作,则会异常。

1.动态的给这个消息添加一个实现方法

对象在接收到未知的消息时,会调用
+ (BOOL)resolveInstanceMethod:(SEL)sel ;如果是类方法,则是+ (BOOL)resolveClassMethod:(SEL)sel
在该方法中,我们可以将实现了的方法添加到这个消息里面
  + (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(w)) {
    class_addMethod([self class], sel, (IMP)w, "v@:@");
//找到了方法然后添加了实现,返回yes。
   return YES;
}
return [super resolveInstanceMethod:sel];
}
void w(id self,SEL _cmd, NSString * name){
NSLog(@"%@",name);
}

这里需要理解一下class_addMethod
先看一下官方文档怎么说

官方对这个方法的描述

首先,
第一个参数,是要添加的类,自然要传[self class]
第二个参数 name,是方法的名称,也就是SEL的类型,用@selector可以获取
第三个参数imp,也就是要给这个方法添加的实现函数,文档里面说了,这个函数必须要接受两个参数,一个是self,一个是_cmd
第四个参数:types,对imp的描述。至少有三个参数:第一个是返回类型,第二个是self,第三个是sel类型的_cmd,列子中用的是“v @:@”v代表,返回类型void
,@代表idleix,:代表sel类型,第二个@代表nsstring类型。
这里面的转换类型,可以在官方文档中搜Type Encodings查找

2,如果在上一步没有做任何处理,runtime会执行这个方法,尝试把这个消息发给另一个对象来执行。

- (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector(setBackgroundColor:)) {
    return label;
}
return [super forwardingTargetForSelector:aSelector];

}
这一步只合适于我们将消息转发到另一个能处理该消息的对象,但是不能对该消息做任何处理

3,如果上一步还不能处理,则会走这个方法

- (void)forwardInvocation:(NSInvocation *)anInvocation 
//抛出异常之前,会给对象发送这个消息forwardInvocation
//anInvocation这个参数包括selector,目标(target)和参数
- (void)forwardInvocation:(NSInvocation *)anInvocation 
//抛出异常之前,会给对象发送这个消息forwardInvocation
这个参数包装了原始消息和对应的参数
  SEL sel = anInvocation.selector;
   if ([label respondsToSelector:sel]) {
  [anInvocation invokeWithTarget:label];把消息转发给label
   }else {
  [self doesNotRecognizeSelector:sel];
  }
  }

用这个方法的时候,我们必须要重写另一个方法:
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
因为上一个方法中的参数使用从这个方法创建的NSInvocation对象,所以必须要重写方法。
- (NSMethodSignature *)methodSignatureForSelector: (SEL)aSelector {
NSMethodSignature *s = [super methodSignatureForSelector: aSelector];
if (!s ) {
s = [label methodSignatureForSelector:aSelector];
}
return s;
}
这里是需要重新给method签名,method里面包三个对象:一个sel,一个method_types,一个是imp,sel是根据方法名和参数类型生成唯一识别,method_type包括了消息的所有参数类型(包括两个隐藏参数:一个self,一个_cmd也就是SEL类型),imp就是函数指针。

消息转发的过程就是这样的。

下面介绍一下上面提到的SEL,IMP,Method

SEL

SEL是表示一个方法的selector指针,oc会根据不同方法的名字,参数序列,生成唯一的标识,也就是这个指针的地址,所以就不难理解为什么oc中不能定义参数不同,但是方法名相同的方法了。

所以我们现在知道了,SEL是一个指向方法的指针。 对于我们而言,也就是这个方法的函数名(唯一辨识);
我们可以通过@selector()或者NSSelectorFromString()得到这个指针;

IMP

上文提到,IMP是一个函数指针,指向方法实现的首地址。通过SEL能够找到对应的IMP。

Method

Method表示方法,包含
一个SEL
一个 method_types
一个 IMP
这里就相当于SEL和IMP之间有了映射。

所以现在不难了解开头说的,消息与方法的绑定(objc_msgSend(receiver,selector,参数···)),也是运行时才确定
1,首先找到这个selector对应的方法实现。因为不同的类中对同一方法有不同的实现,需要根据接受者找到确切的实现。
2,找到了SEL之后,找到IMP,然后将接受者对象本身,以及SEL本身和其他参数传给他
3,将IMP的返回作为这个绑定操作的返回。
所以也不难理解,为什么[self class]和[super class]会打印出self这个子类。因为在super中调用的这个class方法,传入的第一个参数self是子类。

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

推荐阅读更多精彩内容