LC_LOAD_DYLIB和LC_LOAD_WEAK_DYLIB
背景
我们的app版本 最低支持版本为iOS9,然后最近苹果审核,由于国内运营商对苹果施加压力,使用CallkitApi的核心功能的App都会被拒绝过审,当然使用了非核心功能(检测通话状态等苹果提示让使用Callkit框架的)也会被误伤,大概就是所有使用Callkit的都会被拒审,但是可以通过回复邮件告知我们使用的非核心功能,只是苹果推荐要求的新的API,就可以正常过审.
为了防止之后还遇到这个审核问题,我验证了使用旧Api检测通话状态,还可以使用,就将我们私有库podSpec库里面对Callkit库的依赖和相关代码全部下掉删除,然后放到下个版本正常上线.过审.
但是最近突然有客户反馈,iOS9的用户打不开了.启动app都无法打开,这个让我突然很懵逼,然后去确认问题
解决无法启动的过程
1.尝试iOS9,发现启动异常,无法启动,查看错误信息为:
这个时候,mach-O文件其实没有完整的load结束,所以bugly等检测工具是不会监测到的
但是这个时候就很好奇了,我已经下掉了callKit了,怎么还会报callkit的调用呢,然后通过查看Mach-O头文件,发现的确还有link这个库(把这个时候的版本暂时叫做 引入未使用版本),然后到这个依赖方(只是私有库设置了依赖并没有使用),申请下掉了这个依赖库,然后继续打包,问题解决了.
强引用的famework
2.这个过程中,有一个大问题,为什么只会使用Callkit的API的时候iOS9的用户可以正常启动,现在没有人使用了,引入这个库反而启动不了了呢 经过对比之前的正常启动的版本,和上面的 引入未使用的版本的头文件,发现了一个关键信息:
弱引用的famework发现这个信息,就是导致上面的那个问题的根本原因,也引入了这次我们聊到的这2个定义的关键信息
LC_LOAD_DYLIB和LC_LOAD_WEAK_DYLIB
LC_LOAD_DYLIB:
类似于强引用当前库,如果无法成功load当前库,就会报错,导致启动失败,应该也对应于工程target目录下的Build Phases下的Link Binary With Libraries下 库的status->Required
LC_LOAD_WEAK_DYLIB: :
类似于弱引用,如果加载路径下有这个库,就引入,否则可以不引入,不影响使用,类似上图中的optional,但是一旦使用的时候没有这个库,也会报异常
,还有其他的一些类型:
苹果源码
系统选择load的方式 (以下讨论为系统库,非私有三方库)
通过个人验证,发现默认系统引入的库,如果引入的库在当前支持的最低版本之上(存在可能低版本系统不存在这个库的场景)并且引入的库的类在工程中有使用,仅仅声明属性不可以,需要创建一个实例,那么就会被系统置为LC_LOAD_WEAK_DYLIB,但是如果只是引入,没有使用,那么仍旧是LC_LOAD_DYLIB(可能是考虑到了runtime动态创建类特性,防止找不到,不确定),如果引入的库刚好和最低支持的系统版本一致,那么就是正常的LC_LOAD_DYLIB,个人猜测可能系统在编译后,静态链接的时候,做了此优化,通过编译阶段发现有引入这个库的内容,来修改这个链接方式
@interface NSNoticeCall
@property (nonatomic, strong) CXCallObserver *callObserver; // 这种不会影响库的引用方式,还是强引用
@end
- (void)viewDidLoad {
[super viewDidLoad];
[[CXCallObserver alloc]init]; // 这种是弱引用
}
以私有pod库和壳工程混合 以及是否有具体使用库的场景,共下面8种组合
私有库是否依赖 | 主工程是否依赖 | 代码是否使用 | 是否强链接 |
---|---|---|---|
是 | 是 | 是 | 否 |
是 | 是 | 否 | 是 |
是 | 否 | 是 | 否 |
是 | 否 | 否 | 是 |
否 | 是 | 是 | 否 |
否 | 是 | 否 | 取决于主工程里面设置的类型 |
否 | 否 | 是(runtime引用) | 没有引入,取决于系统库还是非系统库,系统的是动态库,可以自动取 |
否 | 否 | 否 | 没有引入 |
而且主工程设置required或者optional 没有太大影响,还是取决于是否有库内类的使用
附带参考:
苹果ImageLoaderMachO源码地址