iOS 金三银四涨薪系列(4) runtime & 消息机制

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

runtime

OC是动态运行时语言是什么意思?

  • 动态类型:运行时确定对象的类型,编译时期能通过,但不代表运行过程中没有问题
  • 动态绑定:运行时才确定对象调用的方法(消息转发)
  • 动态加载:动态库的方法实现不拷贝到程序中,只记录引用,直到使用相关方法的时候才到库里面查找方法实现

runtime能做什么?

  • 获取类的成员变量、方法、协议
  • 为类添加成员变量、方法、协议
  • 动态改变方法实现

class_copyIvarListclass_copyPropertyList的区别?

  • 1、class_copyIvarList可以获取.h.m中的所有属性以及@interface大括号中声明的变量,获取的属性名称有下划线(大括号中的除外)。
  • 2、class_copyPropertyList只能获取由@property声明的属性(包括.m),获取的属性名称不带下划线。

class_ro_tclass_rw_t的区别?

  • class_rw_t提供了运行时对类拓展的能力,class_rw_t结构体中存储了class_ro_t
  • class_ro_t存储的是类在编译时已经确定的信息,是不可改变的。
  • 二者都存有类的方法、属性(成员变量)、协议等信息,不过存储它们的列表实现方式不同。简单的说class_rw_t存储列表使用的二维数组,class_ro_t使用的一维数组。
  • 运行时修改类的方法,属性,协议等都存储于class_rw_t

什么是 Method Swizzle(黑魔法),什么情况下会使用?

  • Method Swizzle 是改变一个已存在的选择器(SEL)对应的实现(IMP)的过程。
  • 类的方法列表存放着SEL的名字和IMP的映射关系。
  • 开发者可以利用 method_exchangeImplementations 来交换2个方法中的IMP
  • 开发者可以利用 method_setImplementation 来直接设置某个方法的IMP
  • 这就可以在运行时改变SELIMP的映射关系,从而实现方法替换。

Method Swizzle注意事项

  • 为了确保Swizzle Method方法替换一定被执行调用,可以在load中执行
  • +load里面使用的时候不要调用[super load]。如果多次调用了[super load],可能会出现“Swizzle无效”的假象
  • 避免调用[super load]导致Swizzling多次执行,在load中使用dispatch_once确保交换只被执行一次。
  • 子类替换没有实现的继承方法,会替换掉父类中的实现,影响父类及其他子嘞
  • +initialize 里面使用要加dispatch_once
  • 进行版本迭代的时候需要进行一些检验,防止系统库的函数发生了变化

如何hook一个对象的方法,而不影响其它对象

  • 方法1:新建一个子类重写方法
  • 方法2:让这个对象的类遵循某个协议,hook时判断。弊端是其他对象遵循了这个协议会受到影响。
  • 方法3:运行时创建一个新的子类,修改对象 isa 指针指向子类,hook 时使用 isKindOf 判断类型

消息发送

消息机制

  • 1、快速查找,方法缓存
  • 2、慢速查找,方法列表
  • 3、消息转发
    • 3-1、方法的动态解析,resolveInstanceMethod
    • 3-2、快速消息转发,forwardingTargetForSelector
    • 3-3、标准消息转发,methodSignatureForSelector & forwardInvocation

objc中向一个nil对象发送消息将会发生什么?

  • 在寻找对象的isa指针时,返回地址0x0,不回做任何操作,也不会有任何错误。

objc在向一个对象发送消息时,发生了什么?

  • 方法调用实际上是发送消息,通过调用 objc_msgSend()实现的。
  • 首先,通过objisa指针找到对应的class
  • 然后,开启快速查找流程。在class的缓存方法列表(objc_cache)里查找方法,如果找到就直接返回对应IMP
  • 如果在缓存中找不到,开始慢速查找流程。在classMethod List查找对应方法,找到了返回对应IMP
  • 都找不到就会走消息转发流程

_objc_msgForward 函数是做什么的?

  • _objc_msgForward用于消息转发:向一个对象发送一条消息,但它并没有实现的时候,就调用_objc_msgForward尝试做消息转发。

为什么需要做方法缓存?

  • 每次执行这个方法的时候都查一遍Method List太消耗性能。
  • 使用objc_cache把调用过的方法做一个缓存, 把method_name作为keymethod_IMP作为value
  • 下次接收到消息的时候,直接通过objc_cache去找到对应的IMP即可, 避免每一次都去遍历objc_method_list

一直都找不到方法怎么办?

  • 会触发消息转发机制,我们一共有三次机会补救以防止crash
  • 方法的动态解析,通过resolveInstanceMethod添加一个IMP使其执行。
  • 快速消息转发,在 forwardingTargetForSelector返回一个可以执行该方法的对象。
  • 标准消息转发,methodSignatureForSelector创建相同方法类型的方法签名(NSMethodSignature),然后重写forwardInvocation并把拥有该签名的方法赋值到anInvocation.selector

消息转发机制的优劣

  • 优点:消息转发机制提供了找不到方法时的补救机会。
  • 缺点:一般情况下会在基类做crash处理,那么有可能把一部分的crash忽略过去导致无法暴露问题。

IMPSELMethod的区别和使用场景

  • SEL相当于一个代号,方便查找方法的代号,处理通知/定时器等都会用到
  • IMP是指向方法实现的指针,动态方法解析的时候会用到
  • Method是一个对象,里面就存有SELIMP,消息转发流程获取方法签名的时候会用到

猜你喜欢

转载自juejin.im/post/7076026684238987300
今日推荐