Runtime常规用法示例

    不论是在面试过程中,还是自己开发项目遇到问题时,runtime经常能解决一些常规情况下遇到的难以解决的部分。    
    今天阅读过一篇关于runtime常规用法的文章,觉得不错便想要通过简化原有文章进行知识存储。   
    文章参考出处:http://www.cnblogs.com/ludashi/p/6294112.html    
    文章参考作者:青玉伏案

    本篇主要讲述了以下几种runtime用法,有一定基础用于提醒自己的玩家可以直接看表格,其他玩家请继续往下看:
用法 关键函数
动态获取类名 const char *class_getName(Class cls)
动态获取类的成员变量 Ivar *class_copyIvarList(Class cls, unsigned int *outCount)、<br />const char *ivar_getName(Ivar v)、<br />const char *ivar_getTypeEncoding(Ivar v)
动态获取类的属性列表 objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)、<br />const char *property_getName(objc_property_t property)
动态获取类的实例方法列表 Method *class_copyMethodList(Class cls, unsigned int *outCount)、<br />SEL method_getName(Method m)
动态获取类所遵循的协议列表 Protocol * __unsafe_unretained *class_copyProtocolList(Class cls, unsigned int *outCount)、<br />const char *protocol_getName(Protocol *p)
动态添加新的方法 Method class_getInstanceMethod(Class cls, SEL name)、<br />IMP method_getImplementation(Method m)、<br />const char *method_getTypeEncoding(Method m)、<br />BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
类的实例方法实现交换 Method class_getInstanceMethod(Class cls, SEL name)、<br />void method_exchangeImplementations(Method m1, Method m2)
动态属性关联 void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)、<br />id objc_getAssociatedObject(id object, const void *key)
消息发送与消息转发机制 + (BOOL)resolveInstanceMethod:(SEL)sel、<br />- (id)forwardingTargetForSelector:(SEL)aSelector、<br />- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector、<br />- (void)forwardInvocation:(NSInvocation *)anInvocation

    我们首先封装一个测试类TestClass,其中需要包含遵守协议,并添加公有属性、私有属性、私有成员变量、公有实例方法、私有实例方法、类方法等。    
    这些内容的添加主要便于之后的测试。
TestClass方法变量声明.png
TestClass私有变量.png
TestClass方法实现.png

一、动态获悉类结构

1. 动态获取类名

    采用`class_getName(cls)`在运行时获取类的名称。将char类型的指针转换成NSString类型进行返回。
获取类名.png

2. 动态获取成员变量

    采用`class_copyIvarList(cls, count)`获取成员变量列表。使用`ivar_getName(variable)`来输出成员变量名称,`ivar_getTypeEncoding(variable)`来输出成员变量类型。    
    我们通过将所得数据组合成NSDictionary来存储单个变量,若干个字典组成NSArray作为属性列表的返回。
获取成员变量.png
    使用TestClass进行用例测试。由于是调用上述方法获取TestClass的成员变量,到了运行时阶段实际就不存在公有私有之分。OC中的类在ARC情况下添加的属性,其实就是自动生成其get方法与set方法。    
    所有获取的成员列表中肯定带有成员属性,成员属性的名称前方带有下划线用于成员变量进行区分。    
    下方中各基本类型由特殊字母代替,可以看出i代表int类型,c代表bool类型,d表示double类型,f表示float类型。而如果是引用类型则直接是一个字符串显示,比如NSString类型就是@"NSString"。
测试成员列表打印.png

3. 动态获取成员属性列表

    上方获取了类的成员变量,那么下方进行属性列表的获取。属性区分于变量主要是它们拥有完整的set方法和get方法。    
    我们使用`class_copyPropertyList(cls, count)`来获取属性列表,通过`property_getName(property)`来获取属性名称。
获取属性列表.png
    下方dynamic的属性是我们使用runtime进行动态添加的。
测试属性列表打印.png

4. 获取类的实例方法

    我们通过`class_copyMethodList(cls, count)`来获取实例方法列表,通过`method_getName(method)`来获取实例方法名称。
获取实例方法.png
    下方打印了所有TestClass类的实例方法,当然包括成员属性的set方法和get方法。**其中`.cxx_destruct`方法不确认归属于何处,也许dealloc方法的自我实现?**
测试实例方法列表打印.png

5. 获取类的协议列表

    我们使用`class_copyProtocolList(cls, count)`来获取协议列表,使用`protocol_getName(protocol)`来获取协议名称
获取协议列表.png

二、动态操作类方法

1. 动态添加方法实现

    其添加原理旨在使用`class_getInstanceMethod(cls, methodName)`获取相关的方法声明以及使用`method_getImplementation(method)`获取相关的方法实现。将它们进行组合后,使用`class_addMethod(cls, methodName, method, type)`进行方法的添加。
动态添加方法实现.png

2. 实现方法交换

    通过`class_getInstanceMethod(cls, methodName)`获取到需要交换的两个方法,直接使用`method_exchangeImplementation(methodA, methodB)`进行方法替换即可。
实现方法交换.png
    通过类目为测试类封装一个针对交换方法的测试用例。
  • 如果是普通情况下,没有交换。在replaceMethod中调用本身势必会造成死循环。
  • 如是如果交换方法成功,那么此时在replaceMethod中调用replaceMethod,其实此时调用的是exchangeMethodA。由于exchangeMethodA不存在死循环,故在测试时,调用了封装的交换方法后,进一步又调用了replaceMethod,其实只是调用了exchangeMethodA而已。
    交换方法的封装.png

三、属性关联

    属性关联可以说是runtime最普通的打开方式了。通过为属性声明一个静态名称,调用`void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)`实现新增属性的set方法,调用`id objc_getAssociatedObject(id object, const void *key)`实现新增属性的get方法即可。
动态添加属性.png

四、消息处理与消息转发

  • 消息处理过程:
  • 当你调用一个类的方法时,先在本类中的方法缓存列表中进行查询,如果在缓存列表中找到了该方法的实现,就执行;如果找不到就在本类中的方法列表中进行查找。
  • 在本类方法列表中查找到相应的方法实现后就进行调用,如果没找到,就去父类中进行查找;如果在父类中的方法列表中找到了相应方法的实现。

当在方法缓存列表,本类中的方法列表以及父类中的方法列表中都找不到相应的实现,到程序崩溃以前还会经历以下过程:

  1. 消息处理
  • 如果一直寻找方法直到父类中都找不到方法实现时会执行+ (BOOL)resolveInstanceMethod:(SEL)sel类方法。
  • 如果返回NO,则表明不做任何处理,继续下一步。如果返回YES,就说明该方法中对找不到实现的方法进行了处理。
  • 我们就可以在此方法中为找不到实现的SEL动态添加一个方法实现,添加完毕后,就会执行我们添加的方法实现。
  • 下一次程序再找不到该类某个方法的实现时,就不会因为找不到而崩溃了。
消息处理.png

2.消息转发

  • 如果不对上述消息进行处理的话,也就是+ (BOOL)resolveInstanceMethod:(SEL)sel方法返回NO时。便进入了下一步消息转发。
  • 即执行- (id)forwardingTargetForSelector:(SEL)aSelector方法。该方法会返回一个类对象,该类的对象有SEL对应的实现,当调用这个找不到方法时,就会转发到ExtClass中进行处理。
  • 此时完成消息转发。如果该方法返回self或者nil,说明不对相应的方法进行转发,那就再走下一步。
消息转发.png

3.消息常规转发

  • 如果不将消息转发给其他类的对象,则此时代表自己进行处理。即上述的方法中返回self或者nil。
  • 此时执行- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector来获取方法的参数以及返回数据类型,即可以理解为该方法的签名。

  • 如果此时再次返回nil,那么消息转发结束。程序崩溃,报出找不到相应的方法实现的崩溃消息。

  • 下方方法执行的先决条件,是要在+ (BOOL)resolveInstanceMethod:(SEL)sel中返回NO。然后下方也是进行将方法转给ExtClass的实现。

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

推荐阅读更多精彩内容

  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,670评论 0 9
  • 我们常常会听说 Objective-C 是一门动态语言,那么这个「动态」表现在哪呢?我想最主要的表现就是 Obje...
    Ethan_Struggle阅读 2,159评论 0 7
  • 原文出处:南峰子的技术博客 Objective-C语言是一门动态语言,它将很多静态语言在编译和链接时期做的事放到了...
    _烩面_阅读 1,210评论 1 5
  • 这篇文章完全是基于南峰子老师博客的转载 这篇文章完全是基于南峰子老师博客的转载 这篇文章完全是基于南峰子老师博客的...
    西木阅读 30,521评论 33 466
  • 第九十二课 背景颜色 1、如何设置标签的背景颜色在CSS中有一个background-color:属性,就是专门用...
    S大偉阅读 251评论 0 0