iOS-编译Category遇到的问题

问题

一个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
2
3
4
5
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.

For example, if main.m includes the code [[FooClass alloc] initWithBar:nil]; then main.o will contain an undefined symbol for FooClass, but no linker symbols for the -initWithBar: method will be in main.o.

Since categories are a collection of methods, using a category's method does not generate an undefined symbol. This means the linker does not know to load an object file defining the category, if the class itself is already defined. This causes the same "selector not recognized" runtime exception you would see for any unimplemented method.

最终的结果是,在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

https://www.jianshu.com/p/4a09d5ebdc2c

https://blog.csdn.net/fengsh998/article/details/8614486