iOS内功篇:runtime

前言

本来打算写一篇关于runtime的学习总结,无奈长篇大论不是我的风格,就像写申论一样痛苦,加之网上关于tuntime的文章多如牛毛,应该也够童子们学习的了,今天就随便聊聊我的理解吧。

runtime是什么

对于初学者,runtime如尼斯湖水怪一样,只存在于传说中,对于开发者,runtime是做好iOS开发,或是深刻掌握Objective C所必需理解的东西。大公司面试都喜欢问:你对runtime熟悉吗?并不是runtime在开发中经常用到,我认为它是OC最核心的部分,只有掌握好它,你才能理解其底层的原理,而不是做一个只会造轮子的码农。要练成盖世神功,需先奠定自身深厚的内功,而tuntime就是iOS开发中的内功。

那么runtime到底是什么鬼?

runtime是一个c和汇编写的动态库(感谢Lision的指正),它就像一个小小的系统,将OC和C紧密关联,这个系统主要做两件事 :
1、封装C语言的结构体和函数,让开发者在运行时创建、检查或者修改类、对象和方法等等。
2、传递消息,找出方法的最终执行代码。

听起来蛮抽象的,我们来点通俗的吧?没问题~~
我们先写一句OC的代码

[zhangsan walkTheDog];

那么在运行时runtime会将它转化成C语言的代码

objc_msgSend(zhangsan, @selector(walkTheDog));

这个方法就是发送消息的方法,类似这样的方法runtime提供了很多,比如:

objc_property_t * class_copyPropertyList ( Class cls, unsigned int *outCount ); // 获取属性列表
Method * class_copyMethodList ( Class cls, unsigned int *outCount );            // 获取所有方法的数组
BOOL class_addMethod ( Class cls, SEL name, IMP imp, const char *types );       // 添加方法

那么我们可以利用这些方法干点什么?

1、遍历对象的属性
比如,看看zhangsan的有哪些属性(身高:180、年龄:18)
2、动态添加/修改属性,动态添加/修改/替换方法
比如,修改zhangsan的身高为190、年龄为20,替换walkTheDog方法(变成walkTheBigDog),给他添加一个新方法(walkTheCat)等等
3、动态创建类/对象/协议等等
比如,创建一个新的对象:lisi
4、方法拦截调用
比如,给zhangsan发送一个walkTheDog消息,但是zhangsan不知道怎么walk啊(没实现该方法),那我们可以拦截下,给该方法动态添加一个实现,甚至可以讲该方法定向或者打包给lisi(其他对象),让lisi来walk。

以上就是runtime的通俗解释,只是稍微举个例子,更多用法大家可以发挥聪明才智,举一反三。

方法调用流程

通俗地讲,调用方法(包含实例方法和类方法)相当于給一个对象发送消息。

所以,实际上,类本身也是一个对象(关于Class这一块就不再这里展开了)。
当我们调用一个方法时,是这样的:
Instance:调用实例方法时,会到对象所属的类的方法列表中查找。
Class:调用类方法时,会到类的metaClass的方法列表中查找。

下面以实例对象调用方法[blackDog walk]为例描述方法调用的流程:

1、编译器会把`[blackDog walk]`转化为`objc_msgSend(blackDog,SEL)`,SEL为@selector(walk)。
2、Runtime会在blackDog对象所对应的Dog类的方法缓存列表里查找方法的SEL
3、如果没有找到,则在Dog类的方法分发表查找方法的SEL。(类由对象isa指针指向,方法分发表即methodList)
4、如果没有找到,则在其父类(设Dog类的父类为Animal类)的方法分发表里查找方法的SEL(父类由类的superClass指向)
5、如果没有找到,则沿继承体系继续下去,最终到达NSObject类。
6、如果在234的其中一步中找到,则定位了方法实现的入口,执行具体实现
7、如果最后还是没有找到,会面临两种情况:
``(1) 如果是使用`[blackDog walk]`的方式调用方法``
``(2) 使用`[blackDog performSelector:@selector(walk)]`的方式调用方法``

第一种情况编译器会报错,第二种需要到运行时才能确定对象能否接收指定的消息,这时候会进入消息转发的流程:

消息转发流程

1、动态方法解析
接收到未知消息时(假设blackDog的walk方法尚未实现),runtime会调用+resolveInstanceMethod:(实例方法)或者+resolveClassMethod:(类方法)
在该方法中,我们可以給未知消息新增一个已经实现了的方法。

void walkFunc(id self, SEL _cmd) {
    //let the dog walk
}

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    NSString * selString = NSStringFromSelector(sel);
    if ([selString isEqualToString:@"walk"]) {
        class_addMethod(self.class, @selector(walk), (IMP)walkFunc, "@:");
    }
    return [super resolveInstanceMethod:sel];
}

2、备用接收者
如果以上方法没有做处理,runtime会调用- (id)forwardingTargetForSelector:(SEL)aSelector方法。
如果该方法返回了一个非nil(也不能是self)的对象,而且该对象实现了这个方法,那么这个对象就成了消息的接收者,消息就被分发到该对象。
适用情况:通常在对象内部使用,让内部的另外一个对象处理消息,在外面看起来就像是该对象处理了消息。
比如:blackDog让女朋友whiteDog来接收这个消息

- (id)forwardingTargetForSelector:(SEL)aSelector {
    NSString * selString = NSStringFromSelector(aSelector);
    if ([selString isEqualToString:@"walk"]) {
        return self.whiteDog;
    }
    return [super forwardingTargetForSelector:aSelector];
}

3、完整消息转发
- (void)forwardInvocation:(NSInvocation *)anInvocation方法中选择转发消息的对象,其中anInvocation对象封装了未知消息的所有细节,并保留调用结果发送到原始调用者。
比如:blackDog将消息完整转发給主人dogOwner来处理

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    if ([DogOwner instancesRespondToSelector:anInvocation.selector]) {
        [anInvocation invokeWithTarget:self.dogOwner];
    }
}

4、如果在以上三个方法都没有处理未知消息,则会引发异常。

后记

本文主要通俗地描述了runtime的概念,并对其主要作用做了简单的概括,旨在給读者抛砖引玉,runtime的奥妙之处就由读者多多探索学习了。
初学者需要更深入地学习:
1、基本概念:Class、Ivar、Method等等
2、消息转发机制
3、在<objc/runtime.h>中理解runtime提供的方法和功能
4、在实际开发中如何灵活运用runtime

^_^相对于理论,程序员总是更擅长用代码来表达,博主接下来将陆续更新runtime实战应用系列文章,敬请关注。

传送门:
iOS runtime实战应用:成员变量和属性
iOS runtime实战应用:关联对象
iOS runtime实战应用:Method Swizzling

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

推荐阅读更多精彩内容

  • 文中的实验代码我放在了这个项目中。 以下内容是我通过整理[这篇博客] (http://yulingtianxia....
    茗涙阅读 914评论 0 6
  • 本文转载自:http://yulingtianxia.com/blog/2014/11/05/objective-...
    ant_flex阅读 751评论 0 1
  • 这篇文章完全是基于南峰子老师博客的转载 这篇文章完全是基于南峰子老师博客的转载 这篇文章完全是基于南峰子老师博客的...
    西木阅读 30,548评论 33 466
  • 转载:http://yulingtianxia.com/blog/2014/11/05/objective-c-r...
    F麦子阅读 729评论 0 2
  • 传送门:http://www.jianshu.com/p/f493bc6a949e/comments/168898...
    永远都能阅读 197评论 0 0