方法交换的坑点和分析
坑点一:交换父类的方法
我们还在刚才的Demo中来演示,现在有一个JQStudent
类了,再创建一个JQPerson
类,让JQStudent
继承JQPerson
,在JQPerson
类添加一个实例方法personInstanceMethod
,在JQStudent
类的load
方法中将jq_studentInstanceMethod
方法和父类中的personInstanceMethod
方法进行交换。
实现代码如下图:
运行结果如下图:
从上面的结果可以看到:
- 子类
JQStudent
对象调用父类JQPerson
的方法personInstanceMethod
,消息发送会通过方法查找从而找到父类方法并调用。 - 但是此时父类
JQPerson
中的方法personInstanceMethod
对应的方法实现已经被交换成了子类JQStudent
的jq_studentInstanceMethod
,因此会执行子类的jq_studentInstanceMethod
方法实现。 - 同理,此时子类中调用
jq_studentInstanceMethod
方法,会执行父类的personInstanceMethod
方法实现。
这样看起来好像没有什么问题啊!紧接着,我们再使用父类JQPerson
对象调用一下personInstanceMethod
方法,如下图:
啪、啪、啪,报错了!!!我们来分析下什么原因,
- 首先,父类调换用
personInstanceMethod
方法会执行子类中的jq_studentInstanceMethod
方法实现。
- 然后又调用了
jq_studentInstanceMethod
方法,但是,此时的调用者是JQPerson
对象,父类JQPerson
中并没有jq_studentInstanceMethod
方法实现。所以因方法找不到而报错。
出了问题,我们来解决以下,将交换方式换成下面这种:
此时,我们的运行不报错了,而且JQStudent
对象调用父类的personInstanceMethod
方法,确实走了方法交换后的流程,JQPerson
对象也正常的调用了personInstanceMethod
方法,互不影响。为什么呢?
原因是:
- 在方法交换前,先尝试给本类添加一下
oriSEL
方法,方法实现为swiMethod
; - 如果添加成功则返回
YES
,代表本类中原本没有oriSEL
的方法实现;接着,再将父类的方法实现oriMethod
替换给本类的swiSEL
; - 添加失败则返回
NO
,代表本类中已有oriSEL
的方法实现,进行正常的方法交换即可。
坑点二:交换的父类中并没有实现的方法
如果要交换的父类方法并没有实现呢?直接看下运行结果:
什么情况?我的天,递归了!!!为什么呢?我们断点调试一下,看图解释:
从上面这些坑中,我们可以得出一些结论:
- 方法交换要遵循功能单一原则,也就是说本类交换本类中的方法,不能影响父类,否则会影响父类和兄弟姐妹的行为(方法);
- 即使要交换父类的方法,也要在本类中实现(重写)父类的方法;
- 本类或父类交换的方法实现不存在,要给本类添加这个方法实现,否则会出现递归调用
基于以上特点,我封装一个更好的方法交换方式,请看以下代码实现:
运行结果如下: