Runtime 知识整理

一、如何在项目中使用runtime

第一步: 在需要使用的类中导入文件

#import

第二步: 在Build Setting中搜索msg,

二、本篇主要讲述了以下几种runtime用法,有一定基础用于提醒自己的玩家可以直接看表格,其他玩家请继续往下看:

用法关键函数

动态获取类名const char *class_getName(Class cls)

动态获取类的成员变量Ivar *class_copyIvarList(Class cls, unsigned int *outCount)、

const char *ivar_getName(Ivar v)、

const char *ivar_getTypeEncoding(Ivar v)

动态获取类的属性列表objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)、

const char *property_getName(objc_property_t property)

动态获取类的实例方法列表Method *class_copyMethodList(Class cls, unsigned int *outCount)、

SEL method_getName(Method m)

动态获取类所遵循的协议列表Protocol * __unsafe_unretained *class_copyProtocolList(Class cls, unsigned int *outCount)、

const char *protocol_getName(Protocol *p)

动态添加新的方法Method class_getInstanceMethod(Class cls, SEL name)、

IMP method_getImplementation(Method m)、

const char *method_getTypeEncoding(Method m)、

BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)

类的实例方法实现交换Method class_getInstanceMethod(Class cls, SEL name)、

void method_exchangeImplementations(Method m1, Method m2)

动态属性关联void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)、

id objc_getAssociatedObject(id object, const void *key)

消息发送与消息转发机制+ (BOOL)resolveInstanceMethod:(SEL)sel、

- (id)forwardingTargetForSelector:(SEL)aSelector、

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector、

- (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

四、消息处理与消息转发

消息处理过程:

当你调用一个类的方法时,先在本类中的方法缓存列表中进行查询,如果在缓存列表中找到了该方法的实现,就执行;如果找不到就在本类中的方法列表中进行查找。

在本类方法列表中查找到相应的方法实现后就进行调用,如果没找到,就去父类中进行查找;如果在父类中的方法列表中找到了相应方法的实现。

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

消息处理

如果一直寻找方法直到父类中都找不到方法实现时会执行+ (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阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容