初探Runtime

用过OC都应该或多或少知道runtime这个黑魔法。OC强大的原因就是运行时这个特性,虽然实际开发中需要用到的runtime知识很少,但是学习它能有助于自己更好的理解OC的语言特性,搞清楚底层的一些相关实现。于是找了一些相关资料。
Objective-C Runtime 运行时之一:类与对象
Objective-C Runtime 运行时之二:成员变量与属性
Objective-C Runtime 运行时之三:方法与消息
Objective-C Runtime 运行时之四:Method Swizzling
Objective-C Runtime 运行时之五:协议与分类
Objective-C Runtime 运行时之六:拾遗
Objective-C 的动态提示和技巧
Objective-C Runtime
Objective-C的hook方案(一): Method Swizzling
关于runtime的资料很多,我觉得这些都是不错的。特别是前几篇循序渐进,讲的比较细致。读了之后虽然还有些不明白的地方,但还是受益匪浅。现在记下一些收获和不解的地方。

首先先了解类的数据结构。它是一个结构体指针,包含了许多信息,其中有一个objc_cache * cache这个是用来缓存该类调用过的方法的,每次调用方法的时候先从方法缓存的结构体的数组开始查询,因为常用的方法就那么一些,这样就避免的每次都去遍历该类的方法列表或者父类的方法列表。从效率上来说要更好一点吧。类的结构体还包含父类,类名,版本,类信息,实例变量的大小,类的成员变量链表,方法定义链表,协议链表就不一一细说了,runtime都提供了相关的API 可以去查询这些信息,不过需要注意的是,我在使用的时候发现有些函数是MRC下才能使用的。
还有一个概念 元类(Meta Class)。当我们向一个对象发送消息时,runtime会在这个对象所属的这个类的方法列表中查找方法;而向一个类发送消息时,会在这个类的meta-class的方法列表中查找。它存储着一个类的所有类方法。任何NSObject继承体系下的meta-class都使用NSObject的meta-class作为自己的所属类,而基类的meta-class的isa指针是指向它自己。

第二篇讲到成员变量和属性 其中有个点是关联对象,这个倒是有用过。当你写了一个类别,想要给他添加属性的时候是不被允许的 。这个时候有个解决方案就是关联对象,运行时添加。将对象通过给定的key连接到类的一个实例上。不过由于是C接口,所有key是一个void指针(const void *)。

方法与消息。有三个概念 Method、SEL、IMP
SEL应该很熟悉,@selector(...) 这个获得的就是SEL,它是一个指向方法的指针。。明显它是由方法名决定的,所有我们在编写代码的时候会发现 在同一个类里不能有两个同名的方法。
IMP 是一个函数指针,指向方法实现的首地址。取得IMP后,我们就获得了执行这个方法代码的入口点,此时,我们就可以像调用普通的C语言函数一样来使用这个函数指针了。换言之就是跳过了Runtime的消息传递机制,直接执行IMP指向的函数实现,这样会更高效。
Method 是一个结构体,包含了一个SEL与IMP。
typedef struct objc_method *Method; struct objc_method { SEL method_name OBJC2_UNAVAILABLE; // 方法名 char *method_types OBJC2_UNAVAILABLE; IMP method_imp OBJC2_UNAVAILABLE; // 方法实现 }
理解这几个术语之间的关系最好的方式是:一个类维护一个运行时可接收的消息分发表;分发表中的每个入口是一个方法(Method),其中key是一个特定名称,即选择器(SEL),其对应一个实现(IMP),即指向底层C函数的指针。

方法调用流程: 在Objective-C中,消息直到运行时才绑定到方法实现上。编译器会将消息表达式[receiver message]转化为一个消息函数的调用,即objc_msgSend.这个函数首先去找selector对应的方法实现,如果没找到,然后就通过父类的指针找到父类,然后在父类的方法list找。如果没有找到就一直沿着继承体系找,直到NSObject。如果最后还是没有定位到selector就会走消息分发流程。
[object message] 如果object无法响应的话,编译器会报错
performSelector 这个是运行时才确定object能否接受message消息
当object无法接受message消息的时候就会启动消息转发的机制。
消息转发的机制基本分三步:
1.动态方法解析
2.备用接收者
3.完整转发
动态方法解析 :当对象接受到未知的消息的时候,首先会调用所属类的的类方法+ (BOOL)resolveInstanceMethod:(SEL)sel 在这里我们可以让它调用我们指定的方法,或者动态添加一个方法来调用。
备用接收者 :如果上一步无法处理消息,则runtime会继续调以- (id)forwardingTargetForSelector:(SEL)aSelector 如果实现了这个方法,可以返回一个非nil的结果。然后这个对象就会作为消息的新接收者。且消息会被分发到这个对象,当然这个对象不能是self自身,否则就会出现无限循环。
完整转发 :这一步是给消息接收者最后一次机会将消息转发给其他对象。调用
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector对象会创建一个表示消息的NSInvocation对象,把与尚未处理的消息有关的全部细节都封装在anInvocation中,包括selector,目标(target)和参数。然后我们可以在forwardInvocation方法中选择将消息转发给其它对象。

Method Swizzling
简单的来说,Method Swizzling 从字面意思理解就是方法互调,利用Runtime提供的函数,将对应的方法实现换掉。明显可以用来重写自带的方法。由于method swizzling会影响到类的全局状态,因此要尽量避免在并发处理中出现竞争的情况。Swizzling应该总是在dispatch_once中执行
+load会在类初始加载时调用
+initialize会在第一次调用类的类方法或实例方法之前被调用
对比之下,发现在+load重写更合适。+initialize有可能不被调用会出现一些不可估计的问题。
Swizzling通常被称作是一种黑魔法,容易产生不可预知的行为和无法预见的后果。

关于Super struct objc_super { id receiver; Class superClass; };
receiver:即消息的实际接收者与self相同
superClass:指针当前类的父类
调用父类方法,会用到super。self是类的一个隐藏参数,每个方法的实现的第一个参数即为self。而super并不是隐藏参数,它实际上只是一个”编译器标示符”,它负责告诉编译器,当调用viewDidLoad方法时,去调用父类的方法,而不是本类中的方法。而它实际上与self指向的是相同的消息接收者。发送消息时,不是调用objc_msgSend函数,而是调用objc_msgSendSuper函数。
[super viewDidLoad]; objc_msgSendSuper ( struct objc_super *super, SEL op, ... ); objc_msgSend(objc_super->receiver, @selector(viewDidLoad))
objc_msgSend(self, @selector(viewDidLoad))

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

推荐阅读更多精彩内容

  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,690评论 0 9
  • 我们常常会听说 Objective-C 是一门动态语言,那么这个「动态」表现在哪呢?我想最主要的表现就是 Obje...
    Ethan_Struggle阅读 2,182评论 0 7
  • 这篇文章完全是基于南峰子老师博客的转载 这篇文章完全是基于南峰子老师博客的转载 这篇文章完全是基于南峰子老师博客的...
    西木阅读 30,548评论 33 466
  • 继上Runtime梳理(四) 通过前面的学习,我们了解到Objective-C的动态特性:Objective-C不...
    小名一峰阅读 744评论 0 3
  • 转载:http://yulingtianxia.com/blog/2014/11/05/objective-c-r...
    F麦子阅读 729评论 0 2