Crash问题总结与防护--unrecognized selector crash

产生原因

​ 因为一个对象调用了一个不属于它方法的方法导致的。

eg:UIButton 添加了点击事件没有实现

方法调用在运行时的过程如下:

  1. 首先,在相应操作的对象中的缓存方法列表中找调用的方法,如果找到,转向相应实现并执行。
  2. 如果没找到,在相应操作的对象中的方法列表中找调用的方法,如果找到,转向相应实现执行
  3. 如果没找到,去父类指针所指向的对象中执行1,2.
  4. 以此类推,如果一直到根类还没找到,转向拦截调用,走消息转发机制。
  5. 如果没有重写拦截调用的方法,程序报错。

拦截调用

​ 拦截调用就是,在找不到调用的方法程序崩溃之前,你有机会通过重写 NSObject的四个方法来处理:

NSObject.png
  1. 调用resolveInstanceMethod给个机会让类添加这个实现这个函数
  2. 调用forwardingTargetForSelector让别的对象去执行这个函数
  3. 调用forwardInvocation(函数执行器)灵活的将目标函数以其他形式执行。

防护方案

​ 选择了第二步forwardingTargetForSelector来做文章。原因如下:

  1. resolveInstanceMethod需要在类的本身上动态添加它本身不存在的方法,这些方法对于该类本身来说冗余的

  2. forwardInvocation可以通过NSInvocation的形式将消息转发给多个对象,但是其开销较大,需要创建新的NSInvocation对象,并且forwardInvocation的函数经常被使用者调用,来做多层消息转发选择机制,不适合多次重写

  3. forwardingTargetForSelector可以将消息转发给一个对象,开销较小,并且被重写的概率较低,适合重写

消息转发机制里的三个步骤处理未知选择,步骤越往后,处理消息的代价就 越大。但是步骤越往前,我们越有可能拦截到系统的本来能处理的方法。