RunTime的原理和问题

RunTime

    是一套C语言的API,它把一些工作放在代码运行时才处理,而并非是编译时,即很多类和成员变量在我们编译的时候是不知道的,在运行时,才会被转换成完整的确定的代码运行。因此,需要一个运行时的系统,来处理编译后代码转换的过程。

OC怎么与运行时交互?

    1、与OC源代码交互。编译器把编写好的OC源代码转换成C语言运行时的代码后,runtime负责在运行时通过传递消息的方式调用方法,确定数据结构和函数。

    2、Foudation框架下NSObject中定义的方法。

    NSObject只定义了完成某些事情的模板,子类继承定义的方法,然后子类可以重新实现。

   还有一些NSObject方法可以从runtime的系统中获取,如(-class返回对象的类。-isKindOfClass:是否是子类。-isMemberOfClass: ( ) 是否是类的成员变量。-respondsToSelector:是否响应了指定的消息。-conformsToProtocol:是否遵循了该协议。-methodForSelector:查询提供方法实现的地址。

3、直接调用RunTime库。

    RunTime库是一个包含一系列方法和数据结构动态共享库,这里面许多方法允许你使用C语言来实现。也可以实现与OC相同的功能,编译器在你写OC代码时显示出runtime是怎样工作的。

怎么检验runtime转化成C语言:

    使用终端命令行切换到mian.m文件所在的目录下并执行: clang -rewrite-objc main.m在mian.m文件目录所在的位置会有一个 main.cpp文件,打开文件可以看到OC代码都被转换成运行时runtime代码了。

RunTime的消息转发机制

    编译器把调用方法转换成objc_msgSend函数调用,需要调用方法的对象根据方法编号SEL去映射表查找对应的方法实现。即动态绑定的过程。

RunTime的应用实例:

     1.动态交换两个方法的实现。
     2.动态添加对象的成本变量、属性和成员方法。(为类别添加属性(我们知道类别是不能扩展属性的,只能扩展方法,但可以运行时可以实现,通过为类增加属性))
     3.获取某个类的所有成员变量和成员方法

4.获取一个类遵循的所有协议。
     5.实现NSCoding的自动归档和自动解档
     6.实现字典和模型的自动转换。(MJExtension 字典转模型实现)

RunTime的问题:

1、RunTime怎么添加属性和方法?

   ivar表示成员变量,可以调用底层的class_addIvar、class_addMethod、class_addProperty、class_addProtocol、class_replaceProperty方法,动态的添加。

2、runtime 如何实现 weak 属性?

   weak策略表明该属性定义了一种“非拥有关系”。为这种属性设置新值时,设置方法既不保留新值,也不释放旧值。此特质同assign类似;然而在属性所指的对象遭到摧毁时,属性值也会清空(nil out)

   那么runtime如何实现weak变量的自动置nil?

    runtime对注册的类,会进行布局,会将 weak 对象放入一个 hash 表中。用 weak 指向的对象内存地址作为 key,当此对象的引用计数为0的时候会调用对象的 dealloc 方法,假设 weak 指向的对象内存地址是a,那么就会以a为key,在这个 weak hash表中搜索,找到所有以a为key的 weak 对象,从而设置为 nil。

3. runtime如何通过selector找到对应的IMP地址?(分别考虑类方法和实例方法)

   每一个类对象中都一个对象方法列表,类方法列表是存放在类对象中isa指针指向的元类对象中,方法列表中每个方法结构体中记录着方法的名称,方法实现,以及参数类型,其实selector本质就 当我们发送一个消息给一个类时,这条消息会在类的Meta Class对象的方法列表里查找。

4. 使用runtime Associate方法关联的对象,需要在主对象dealloc的时候释放么?

    无论在MRC下还是ARC下均不需要被关联的对象在生命周期内要比对象本身释放的晚很多,它们会在被 NSObject的-dealloc 调用的object_dispose()方法中释放。释放步骤:1、调用 -release :引用计数变为零。2、 父类调用 -dealloc  3、NSObject 调 -dealloc 4. 调用 object_dispose()

5、简述下Objective-C中调用方法的过程

    Objective-C是动态语言,每个方法在运行时会被动态转为消息发送,即:objc_msgSend(receiver,selector),

    整个过程介绍如下:1、objc在向一个对象发送消息时,runtime库会根据对象的isa指针找到该对象实际所属的类。2、然后在该类中的方法列表以及其父类方法列表中寻找方法运行。3、如果,在最顶层的父类(一般也就NSObject)中依然找不到相应的方法时,程序在运行时会挂掉并抛出异常unrecognizedselector sent to XXX。3、但是在这之前,objc的运行时会给出三次拯救程序崩溃的机会。

    Runtime 铸就了Objective-C 是动态语言的特性,使得C语言具备了面向对象的特性,在程序运行期创建,检查,修改类、对象及其对应的方法,这些操作都可以使用runtime中的对应方法实现。

8. 什么是method swizzling(俗称黑魔法)

    简单说就是进行方法交换。在Objective-C中调用一个方法,其实是向一个对象发送消息,查找消息的唯一依据是selector的名字。利用Objective-C的动态特性,可以实现在运行时偷换selector对应的方法实现,达到给方法挂钩的目的。每个类都有一个方法列表,存放着方法的名字和方法实现的映射关系,selector的本质其实就是方法名,IMP有点类似函数指针,指向具体的Method实现,通过selector就可以找到对应的IMP。

    利用 method_exchangeImplementations 交换两个方法的实现

    利用 class_replaceMethod 替换方法的实现

    利用 method_setImplementation 来直接设置某个方法的IMP

9、对象如何找到对应的方法去调用?

    对象方法保存到类中,类方法保存到元类(meta class),每一个类都有方法列表methodList。明确去哪个类中调用,通过isa指针,

    1、根据对象的isa去对应的类查找方法,isa:判断去哪个类查找对应的方法 指向方法调用的类。

    2、根据传入的方法编号SEL,里面有个哈希列表,在列表中找到对应方法Method(方法名)。

    3、根据方法名(函数入口)找到函数实现,函数实现在方法区。

   (根据对象的 isa 指针找到类对象 id,在查询类对象里面的 methodLists 方法函数列表,如果没有在好到,在沿着 superClass ,寻找父类,再在父类 methodLists 方法列表里面查询,最终找到 SEL ,根据 id 和 SEL 确认 IMP(指针函数),在发送消息;)

10、给NSObject添加一个name属性,动态添加属性 ->runtime ?

    1、给NSObject添加分类,在分类中添加属性。问题:@property在分类中作用:仅仅是生成get,set方法声明,不会生成get,set方法实现和下划线成员属性,所以要在.m文件实现setter/getter方法,用static保存下滑线属性,这样一来,当对象销毁时,属性会一直存在缓存池中,无法销毁。

    2、所以,只能想到用runtime动态添加属性:本质是让属性与某个对象产生一段关联。使用场景:给系统的类添加属性时。(object:保存到哪个对象中 、key:用什么属性保存 属性名、 value:保存值、  policy:策略,strong,weak)

11、为什么动态添加方法?

    如果一个类方法非常多,加载类到内存的时候也比较耗费资源,需要给每个方法生成映射表,可以使用动态给某个类,添加方法解决。

    OC 中我们很习惯的会用懒加载,当用到的时候才去加载它,但是实际上只要一个类实现了某个方法,就会被加载进内存。当我们不想加载这么多方法的时候,就会使用到runtime 动态的添加方法。

12、什么时候会报unrecognized selector错误?iOS有哪些机制来避免走到这一步?

    当发送消息的时候,我们会根据类里面的 methodLists 列表去查询我们要动用的SEL,当查询不到的时候,我们会一直沿着父类查询,当最终查询不到的时候我们会报unrecognized selector 错误,当系统查询不到方法的时候,会调用 +(BOOL)resolveInstanceMethod:(SEL)sel 动态解释的方法来给我一次机会来添加,调用不到的方法。或者我们可以再次使用-(id)forwardingTargetForSelector:(SEL)aSelector 重定向的方法来告诉系统,该调用什么方法,一来保证不会崩溃。

13、能否向编译后得到的类中增加实例变量?能否向运行时创建的类中添加实例变量?为什么?

    1、不能向编译后得到的类增加实例变量 2、能向运行时创建的类中添加实例变量。【解释】:1. 编译后的类已经注册在 runtime 中,类结构体中的objc_ivar_list 实例变量的链表和 instance_size 实例变量的内存大小已经确定,runtime会调用 class_setvarlayout 或 class_setWeaklvarLayout 来处理strong weak 引用.所以不能向存在的类中添加实例变量。2. 运行时创建的类是可以添加实例变量,调用class_addIvar函数. 但是的在调用 objc_allocateClassPair 之后,objc_registerClassPair之前,原因同上.

14、runtime如何实现weak变量的自动置nil?

    runtime 对注册的类, 会进行布局,对于 weak 对象会放入一个hash 表中。 用 weak 指向的对象内存地址作为 key,当此对象的引用计数为0的时候会 dealloc,假如weak 指向的对象内存地址是a,那么就会以a为键,在这个 weak 表中搜索,找到所有以a为键的 weak 对象,从而设置为 nil。


猜你喜欢

转载自blog.csdn.net/pdd_1128/article/details/79991295