类方法转发

本文大约750字,将会介绍对类方法的消息转发,它能帮助我们解决类方法的unrecognized selector crash,或是应对一些其它需要转发的场景

!!!原创文章,转载请注明来源: pany.fun

如果你对消息转发流程还不熟悉,请先行了解 Objective-C 中的消息与消息转发

熟悉以后我们知道,其实消息的转发可以非常简单,简单到可能几行代码就能搞定

- (id)forwardingTargetForSelector:(SEL)aSelector {
    // TODO: forward
    return target;
}

这就是一个最简单的消息转发方案

# 问题

我司最近有一个需求需要用到消息转发,于是我们也是啪啪啪就敲上了上面的几行代码,分分钟搞定需求,一切都很顺利。

但是我们在测试类方法的时候,问题出现了—— forward方法居然没有执行🤔

按到理解,NSObject是root类,那么所有找不到的方法应该都能走到NSObject

于是我们把上面的forward方法写在了NSObject的分类里进行测试 (正常开发不建议在分类中重写如此敏感的方法)

分类中的方法会覆盖原类中的方法,也就是说,我们在分类中写的forward方法能够拿到所有的forward消息

然后我们再次测试类方法,发现即使是分类里的forward方法,也没有被执行,但是测试实例方法确实是能够执行到的

查询NSObject.h也没有发现任何能够简单转发类方法的接口

# 真相

真相当然只有一个,我们怀疑forward没生效是因为消息链路中有可疑人物已经处理了forward,最可疑的当然是NSObject

考虑到我们测试实例方法可行而类方法不可行,我们将目标进一步定位在了NSObject的meta类上

所以我们自然而然的想要给NSObject的原来添加forward方法,于是我们在NSObject的分类中硬塞了一个这样的方法

+ (id)forwardingTargetForSelector:(SEL)aSelector {
    // TODO: forward
    return target;
}

需要注意,虽然它跟前面的forward方法名字完全相同,但是它是类方法,所以它会被添加到NSObject的meta类的方法列表中

再次测试类方法,生效!

真相只有一个:NSObject的meta类中包含方法forwardingTargetForSelector,即NSObject还有一个类方法版本的forward,但是它并没有开放在.h中

# 源码

apple没有开放不用怕,毕竟runtime是开源的,我们查询了NSObject的源码

// Line: 2102

+ (id)forwardingTargetForSelector:(SEL)sel {
    return nil;
}

- (id)forwardingTargetForSelector:(SEL)sel {
    return nil;
}

真相大白

创作不易,转载请注明来源!pany.fun
本文链接:http://pany.fun/post/类方法转发/