iOS 上的自动链接( Auto Linking )

附上文中 demo

背景知识

当我们在 Xcode 的 Build Phases -> Link Binary With Library 中添加一个 framework 的依赖。例如下图:

1.png

Xcode 实际上做的是:

  1. 在链接器链接的时候,添加对应的链接器标志,即-framework LightGameAD 。对应 Build Setting 中的 Other Linker Flags 设置。
  2. 添加对应 framework 的搜索路径,eg: -F/Users/xxx/.../LightGameAD 。对应 Build Setting 中的 Framework Search Paths 设置。

换句话说,当我们需要 link 一个 framework 时,完全也可以通过修改 Build Setting 中的设置来链接,不需要设置 Build Phases -> Link Binary With Library 。事实上,我们常用的 Cocoapod ,就是通过设置 build setting 来设置 framework 的链接的。

.a 静态库也可以通过同样的方式设置,分别对应 Other Linker Flags 的 -lxxx 以及 Library Search Paths(-L/Users/xxx/.../Library目录)。这里有一点和 Framework 不同。
如果静态库有 lib 前缀,eg: libxxx.a,在引入时,对应 Other Linker Flags 的 -lxxx。这也是为什么通过 Cocoapod 导入的 .a静态库,都需要 lib 前缀,否则会报找不到符号。
如果没有lib前缀,那相当于直接将 Other Linker Flags 中添加 /xxx/xxx/xxx.a,也不需要添加 Library Search Paths,因为此时路径已经明确,不需要查找 Library。

同理,当我们需要添加一个系统库的依赖时,因为系统默认会搜索系统库所在目录,所以可以只在 Other Linker Flags 中添加上对应的 flag 即可。eg: -framework UIKit 即可,效果等同于在 Build Phases -> Link Binary With Library 添加 UIKit.framework 。

什么是自动链接?

当目标文件(.o文件)在最终构建阶段链接时,链接器要知道哪些库需要被链接。例如,如果将 #import <AdServices/AdServices> 添加到实现文件(.m文件),我们需要将-framework AdServices 标志添加到链接器。

自动链接旨在帮助我们去掉最后一步。换句话说,它旨在从代码中的导入语句派生库链接器的 flag 标志。开发人员不需要添加任何 framework / Library 的链接器标志,可以通过导入语句直接使用任何 framework / Library。

这里的导入语句,包括在 Objective-C 中使用 #import 和 @import,也包括 Swift 中的 import 。

自动链接的原理

自动链接通过在目标文件中插入链接器标志来工作。当链接器创建最终的可执行文件时,这些链接器标志就好像作为参数传递一样。

链接器标志存储在目标文件中的 LC_LINKER_OPTION Load Command 命令。comand 的结构体如下:

struct linker_option_command {
    uint32_t  cmd;  /* LC_LINKER_OPTION only used in MH_OBJECT filetypes */
    uint32_t  cmdsize;
    uint32_t  count;    /* number of strings */
    /* concatenation of zero terminated UTF8 strings.
       Zero filled at end to align */
};

可以使用 otool -l xxx.o 命令来打印它们:

Load command 5
     cmd LC_LINKER_OPTION
 cmdsize 32
   count 2
  string #1 -framework
  string #2 AppKit
Load command 6
     cmd LC_LINKER_OPTION
 cmdsize 40
   count 2
  string #1 -framework
  string #2 QuartzCore

当然也可以通过一些可视化工具来查看,例如:MachOView、MachO-Explorer 等工具。

Framework vs Dynamic Library

值得注意的是,自动链接可以与任何 Clang 模块一起使用,而不仅仅是 Framework 。例如,/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/include/module.moduleMap 中定义了如下所示的 zlib 模块:

module zlib [system] [extern_c] {
 header "zlib.h"
 export *
 link "z"
}

上述意味着如果我们使用 #import <zlib.h>,打开 Enable Modules 开关,则链接器自动插入-lz。我们可以使用 otool -l 验证此问题:

Load command 4
     cmd LC_LINKER_OPTION
 cmdsize 16
   count 1
  string #1 -lz

控制自动链接开关

仅当 Clang Enable Modules 开关打开时,自动链接才生效。如果你是通过命令行调用 clang,则需要添加 -fmodules 参数。在 Xcode Build Setting 中对应的 key 为 CLANG_ENABLE_MODULES ( Enable Modules (C and Objective-C) )

即使启用了模块,也可以单独禁用自动链接。对应的 clang 命令行参数为 -fno-autolink 。在 Xcode Build Setting 中对应的 key 为 CLANG_MODULES_AUTOLINK ( Link Frameworks Automatically )

2.png

注意事项

  1. 是否启用了自动链接,只和这个源文件被编译的时候的设置有关,和使用的地方是否开启自动链接无关。举个栗子:
    1. 当我们生成一个 target 为 Framework 的工程,这里叫做 ABC.framework 吧。我们的 ABC.framework 依赖了两个系统库 JavaScriptCore.framework 、libz.tbd 。
    2. 如果 ABC.framework 工程开启了自动链接,生成了最终产物 ABC.framework。此时当我们的 ABC.framework 被使用的时候,也就是被另一个 target 引入的时候,可以直接使用,不需要再添加 JavaScriptCore.framework 、libz.tbd 这两个系统库的依赖。即使 target 的自动链接或模块开关关闭,也可以直接使用。
    3. 如果 ABC.framework 工程关闭了自动链接,此时被使用时,就必须添加相应库的依赖。

注意上面的例子,只和 ABC.framework 的设置有关,和使用方的 target 设置无关。这是因为,我们的源文件在被编译的时候,已经根据对应的设置,决定了是否包含链接需要的信息。

  1. 自动链接不仅仅可以用来链接系统库,也同样可以用来链接我们自己生成的 Framework 库。前面说过,自动链接相当于自动帮我们添加了 -framework xxx ,所以我们只需要在 Framework Search Path 中添加对应 Framework 的路径即可。

  2. 自动链接的 Framework 如果找不到,本身并不会编译报错或启动 crash,只会报警告。当然,如果你确实使用了被链接的 Framework 中的符号,还是会报符号找不到的。举个栗子:

    1. iOS14 新出的 AppTrackingTransparency.framework。如果我们的应用需要支持iOS14一下,我们的代码通常会写成如下:
    // iOS14以上,使用 AppTrackingTransparency 框架
    if (@available(iOS 14.0, *)) {
        if ([ATTrackingManager trackingAuthorizationStatus] == ATTrackingManagerAuthorizationStatusNotDetermined) {
            [ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) {
                //处理idfa状态
            }
        } else {
            //处理idfa状态
        }
    } else {
        // iOS14以下,使用 AdSupport 框架
        //处理idfa状态
        // [[ASIdentifierManager sharedManager] isAdvertisingTrackingEnabled]
    }
    
    1. 因为我们代码中已经根据系统版本做了判断,如果使用自动链接,当在14以下版本的时候,自动链接链接不到 AppTrackingTransparency 框架,但是此时因为代码也没有对 AppTrackingTransparency 框架的符号引用,所以不会有任何问题。但是如果是手动依赖链接,则这里一定要设置为 optional,也就是弱链接。如果是 required,会启动崩溃。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,219评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,363评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,933评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,020评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,400评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,640评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,896评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,597评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,327评论 1 244
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,581评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,072评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,399评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,054评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,849评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,672评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,585评论 2 270

推荐阅读更多精彩内容

  • 用到的组件 1、通过CocoaPods安装 2、第三方类库安装 3、第三方服务 友盟社会化分享组件 友盟用户反馈 ...
    SunnyLeong阅读 14,500评论 1 180
  • 什么是动态库? 与静态库相反,动态库在编译时并不会被拷⻉到⽬标程序中,⽬标程序中只会存储指向动态库的引⽤。等到程序...
    帅驼驼阅读 624评论 0 5
  • 模块化之路(实验) 一直以来,我们都在尝试让代码尽可能高内聚、低耦合。比如我们将MVP框架改成MVVM框架,以解决...
    ShayneFcf阅读 2,216评论 1 9
  • 我是黑夜里大雨纷飞的人啊 1 “又到一年六月,有人笑有人哭,有人欢乐有人忧愁,有人惊喜有人失落,有的觉得收获满满有...
    陌忘宇阅读 8,471评论 28 53
  • 首先介绍下自己的背景: 我11年左右入市到现在,也差不多有4年时间,看过一些关于股票投资的书籍,对于巴菲特等股神的...
    瞎投资阅读 5,588评论 3 8