1. 引文
面试官:工程的类ProjectClassA依赖静态库FrameworkA的ClassA对象的methodA方法,methodA依赖静态库FrameworkB的ClassB对象的methodB方法,此时删除methodB的声明跟实现,重新打包FrameworkB,此时Build工程会报错么?为什么?
如果你对这个问题的答案心知肚明的话就可以跳过本文的阅读了(如果方便可以点个赞哈😅)
2.结论推导
这里不去展开讲编译与链接的过程跟原理,网上有很多优质的帖子,相信每个iOSer都能倒背如流(推荐《程序员自我修养》这本书),这里只展开N_UNDF符号相关的部分:
编译
编译成目标文件时.m的外部依赖会生成对应的N_UNDF符号保存在它自身.o文件的符号表中,由后续静态链接时再进行确认
静态链接
静态链接的过程主要包括了地址和空间分配、符号决议和重定位。链接器会全面扫描.o文件,将N_UNDF符号保存保存在U表(N_UNDF符号的集合)中,结束后会去遍历静态库扫描静态库中的.o文件是否包含该N_UNDF符号,如果有则链接进来,此时会有该.o文件新的N_UNDF符号加到U表中,最后如果U表中还有N_UNDF符号的话就会报错Undefined symbol
链接ClassA.o时会将依赖的methodB(N_UNDF)符号加到U表中,因为methodB方法被删除了,最后会找不到methodB的符号,所以我们可以得出结论,上述问题会在静态链接时报错Undefined symbol
以上
-----------------------------------------------------------
如果你也这么觉得的话,那你可能跟我一样缺乏一些细节的理解
让我们来测试一下,实际Build工程会成功,但是当你访问这个方法时会crash
这里就跟上面的结论不一样了,那具体有什么区别呢? 我们来直接看下Mach-O
分析下
3.Mach-O分析
先看下FrameworkB的Mach-O文件
可以发现ClassB.o中__TEXT的__objc_methname中有methodB的字符串,符号表中有methodB(N_SECT)、ClassB(N_EXT)。这里跟预期相同
那再来看下依赖methodB的FrameworkA的Mach-O文件
可以发现ClassA.o中__DATA的__objc_classrefs有ClassB,__TEXT的__objc_methname有methodA跟methodB的字符串
符号表中有methodA(N_SECT)、ClassB(N_UNDF),但并没有methodB相关的符号
这里其实跟我的预期就不符了,我之前一直以为也会生成一个methodB的N_UNDF符号,实际并没有
再来看下依赖methodA的ProjectClassA.o的Mach-O文件
可以看到ProjectClassA的__DATA的__objc_classrefs有ClassA,__TEXT的__objc_methname有自身init跟methodA的字符串。符号表中只有ClassA(N_UNDF)的符号。
这么看来由于ClassA没有生成methodB的N_UNDF符号,套上刚才的静态链接流程结束后确实没有额外的N_UNDF的符号,所以链接会成功。原因应该是OC是一门动态语言,和C语言不一样并不会为依赖的方法生成N_UNDF符号,而是会通过字符串走消息机制的那一套流程,由于我们没有重新生成FrameworkA,ClassA.o中__objc_methname还是有methodB的,这里从Project的Mach-O文件就能看出__TEXT的__objc_methname是有methodB的,但没有methodB的符号跟代码,所以运行时寻找方法是会出现经典的u
nrecognized selector sent to instance Crash。(依赖新的FrameworkB重新编译ClassA肯定是会报错的 No visible @interface for 'ClassB' declares the selector 'methodB')
结论
Objc的method引用并不会生成方法的N_UNDF符号,只会对class会生成N_UNDF符号,其实跟静态库的category不会被链接到Mach-O是一个原理,这点是我早就知道的但没有做到触类旁通
现代工程基本都大量使用静态库去做组件化,那这里就会有个坑,在删除public方法时一定要很小心,理论上要扫描依赖此类的全部静态库,然后都重新打包下才能避免风险(这步可以自动化处理掉)
碎碎念
1.其实我全篇只想讲结论的这一个小点,如果你很明确引言问题的答案的话应该会觉得有些啰嗦,但如果你答错的话希望本文能让你有对这一小点的收货
2.其实还是想逼自己坚持下定期发些东西但奈何实在是挤不出来什么东西来。刚好最近碰到了这个case但是当时也没有很明确搞清楚这些,之前看的很多博客讲解的时候都是以C语言为例子,并没有说OC的不同,后面有空重新摸索了一遍也让自己对这些的理解更深了些,一举多得吧
愿疫情早日结束、世界和平🙏🙏🙏
符号说明见链接:github.com/aidansteele…