问题
一个iOS菜鸟,最近工作中遇到了一个有关Category的问题。Objective-C语言。
遇到问题的原因是,我给AppDelegate加一个Category的时候,忘记Check 中Production的Deployment Target。
造成的结果是,我在Category中加的方法,在运行时怎么都不能被找到,然后就会Crash。运行期间报selector not recognized,不是property找不到,是method。
解决方法当然就是把这个m文件选入Production的Target就好了。这些都很简单。
问题在于,为什么Xcode没有在Compile Time找到这个问题呢?如果我编译,链接PROD的Target,按一般的静态语言,这个错误很容易被发现。所以就跟同事讨论了一下Obj-C的编译与Runtime的问题。
再简单总结一下遇到的情况:
测试1, 如果把普通类(非Category)的m文件从target排除掉,那么compiler在编译期,准确的说链接时,就会报错。
测试2,把Category的m文件从target排除掉,编译期就不会报错,只会在运行到那一行Category的方法时,发现找不到Symbol。
原因
为什么呢?
短的答案是,Category的方法调用不会被链接时确定,而是在Runtime去找。
长的答案参考自Apple文档 https://developer.apple.com/library/archive/qa/qa1490/_index.html
里边这一段说明了情况,是说我们的主类去引用一个Category的method的时候并不能产生一个undefined symbol去给链接器处理
1 | The dynamic nature of Objective-C complicates things slightly. Because the code that implements a method is not determined until the method is actually called, Objective-C does not define linker symbols for methods. Linker symbols are only defined for classes. |
最终的结果是,在RUNTIME用@selector产生SEL的后,在Dispatch Table中找不该SEL ID到IMP
其他参考
在研究有关Category的问题时候,又通过一些博客学习了一些新知识。
https://tech.meituan.com/DiveIntoCategory.html
https://www.jianshu.com/p/5aed848e283d
https://www.zhihu.com/question/27179396
https://hit-alibaba.github.io/interview/iOS/ObjC-Basic/Runtime.html