Runtime 的四个重要的概念—动态类型,动态绑定,动态方法决议,内省

491 阅读4分钟
Objective-C Runtime 的四个重要的概念——动态类型,动态绑定,动态方法决议,内省

动态类型

动态类型(Dynamic typing)是指对象的具体类型在运行时才能确定。

动态绑定

动态绑定(Dynamic binding)是指把消息映射到方法实现的这一过程是在运行时,而不是在编译时完成的。


动态方法决议

动态方法决议(Dynamic Method Resolution)是一种能为方法动态的提供方法实现的能力。

Objective-C 的 @dynamic 关键字就属于动态方法决议,这个关键字告诉编译器这个属性的 setter 和 getter 方法是在运行时动态提供的。

三次消息转发的
第一步 ,动态方法决议

NSObject 包含两个类方法:

+ resolveInstanceMethod:
+ resolveClassMethod

resolveInstanceMethod 为给定的 Selector 提供一个实例方法的实现。
这个方法会在每次实例对象收到消息时调用。resolveClassMethod 为给定的 Selector 给提供一个类方法的实现。
 这个方法会在每次类收到消息时调用

复写这两个方法即可在运行时为实例方法或类方法动态的提供实现。

作用是,当接受者接受到的消息方法并没有找到的情况下,系统会调用该函数,给予这个对象一次动态添加该消息方法实现的机会

第二步 后备接收者对象

-(id)forwardingTargetForSelector:(SEL)aSelector

第一次方法解析无法处理, 就是让别的对象B来处理该问题,如果对象B能够处理该消息,那么该消息转发结束。

第三步 以其他形式实现该消息方法

-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector

-(void)forwardInvocation:(NSInvocation *)anInvocation

  1. 将未知SEL作为参数传入methodSignatureForSelector,在该方法中处理该消息,一旦能够处理,返回方法签名(自由的修改方法签名,apple签名),让后续forwardInvocation来进行处理
  2. forwardInvocation中我们可以做很多的操作,这个方法比forwardingTargetForSelector更灵活

    也可以做到像forwardingTargetForSelector一样的结果,不同的是一个是让别的对象去处理,后者是直接切换调用目标,也就是该方法的Target 类
  3. 我们也可以修改该方法的SEL,就是重新替换一个新的SEL


内省

NSObject API 中提供了对象内省(Introspection)的功能。内省是一种能在运行时检查对象自身信息的能力。

由于 Objective-C 的大量行为都是在运行时完成的,内省能力便至关重要。

NSObject 对象提供的内省方法主要有:

isKindOfClass:
检查对象是否是给定的 Class 的实例或给定 Class 的子类的实例。

respondsToSelector:
检查对象是否能响应某个 Selector。

conformsToProtocol:
检查对象是否符合某个协议。

methodSignatureForSelector:
获取某个 Selector 得方法签名。





方法查找

objc_msgSend会经过以下步骤:

  • 1、通过对象的isa指针找到对象所属的class

  • 2、在class的方法缓存(objc_cache)中查找方法,如果没有找到,则继续3、4步骤

  • 3、在class的method_list中查找方法

  • 4、如果class中没有找到方法,则继续往它的super class中查找, 直到查找到根类

  • 5、一旦找到方法,就去执行对应的方法实现(IMP),并把方法添加到方法缓存中

在这个方法查找过程中,runtime引入了缓存机制,这是为了提高方法查找的效率,因为,如果调用的方法在根类中,那么每次方法调用都要沿着继承链去每个类的方法列表中查找一遍,效率无疑是低下的。这个方法缓存的实现,其实就是一个哈希表的存储,以selector name的哈希值为索引, 存储方法的实现(IMP),这样的查找效率较高,看到这里,可能有人会有疑问,既然每个class维护着一个方法缓存的哈希表,为什么还要维护一个方法列表method list呢?每次直接去哈希表里查找方法不是更快吗?

这其实是因为哈希表是一个无序表,而方法列表是一个有序列表,查找方法会顺着method list依次查找,这样就赋予了category一个特性:可以覆盖原本类的实现,而如果是使用哈希表,则无法保证顺序。关于category的原理和具体实现,将在后续的文章中探讨。