iOS:静态库和dead code strip

1.前沿

.a:常见的静态库文件格式
.dylib:传统意义上的动态库文件格式
.framework:可以是静态库也可以是动态库。
.xcframework:刚推出,不同架构库放到一块。

什么是库
库(Library)说白了就是一段编译好的二进制代码,加上头文件就可以供别 人使用。
什么时候会用到库(Library)?

  1. 某些代码需要给别人使用,但是我们不希望别人看到源码,就需要以
    库的形式进行封装,只暴露出头文件。
  2. 对于某些不会进行大的改动的代码,我们想减少编译的时间,就可以 把它打包成库,因为库是已经编译好的二进制了,编译的时候只需 要 Link 一下,不会浪费编译时间。
    什么是链接(Link)?
    库在使用的时候需要链接(Link),
    链接 的方式有两种:
  3. 静态
  4. 动态
    什么是静态库?
    静态库即静态链接库:可以简单的看成一组目标文件的集合。即很多目 标文件经过压缩打包后形成的文件。Windows 下的 .lib,Linux 和 Mac 下的 .a。Mac独有的.framework。
    缺点:
    浪费内存和磁盘空间,模块更新困难
    什么是动态库?
    与静态库相反,动态库在编译时并不会被拷⻉到目标程序中,目标程序 中只会存储指向动态库的引用。等到程序运行时,动态库才会被真正加 载进来。格式有:.framework、.dylib、.tdb。
    缺点:
    会导致一些性能损失。但是可以优化,比如延迟绑定(Lazy Binding)技术
    什么是tdb格式?
    tbd全称是text-based stub libraries,本质上就是一个YAML描述的文本文
    件。
    他的作用是用于记录动态库的一些信息,包括导出的符号、动态库的架构信息、动
    态库的依赖信息
    用于避免在真机开发过程中直接使用传统的dylib。
    对于真机来说,由于动态库都是在设备上,在Xcode上使用基于tbd格式的伪 framework可以大大减少Xcode的大小。
    Framework
    Mac OS/iOS 平台还可以使用 Framework。Framework 实际上是一种打包 方式,将库的二进制文件,头文件和有关的资源文件打包到一起,方便管 理和分发。
    Framework 和系统的 UIKit.Framework 还是有很大区别。系统的 Framework 不需要拷⻉到目标程序中,我们自己做出来的 Framework 哪怕 是动态的,最后也还是要拷⻉到 App 中(App 和 Extension 的 Bundle 是 共享的),因此苹果又把这种 Framework 称为 Embedded Framework
    Embedded Framework
    开发中使用的动态库会被放入到ipa下的framework目录下,基于沙盒运行。
    不同的App使用相同的动态库,并不会只在系统中存在一份。而是会在多 个App中各自打包、签名、加载一份。

2.链接静态库生成目标文件

2.1.创建一个静态库

创建一个静态库,里面有一个oc类ReplaceAFNetWorking,编译后里面两个文件,一个是libReplaceAFNetWorking.a一个是头文件ReplaceAFNetWorking.h
查看.a文件到底是什么

file libReplaceAFNetWorking.a
// libReplaceAFNetWorking.a: current ar archive random library

2.2查看.a文件的内容

我们查看a文件的内容,需要使用ar命令
查看ar命令作用 man ar

  ar -- create and maintain library archives

SYNOPSIS
     ar -d [-TLsv] archive file ...
     ar -m [-TLsv] archive file ...
     ar -m [-abiTLsv] position archive file ...
     ar -p [-TLsv] archive [file ...]
     ar -q [-cTLsv] archive file ...
     ar -r [-cuTLsv] archive file ...
     ar -r [-abciuTLsv] position archive file ...
     ar -t [-TLsv] archive [file ...]
     ar -x [-ouTLsv] archive [file ...]

ar可以修改查看a文件的内容

ar -t libReplaceAFNetWorking.a
__.SYMDEF SORTED
ReplaceAFNetWorking.o

我们创建的库里面就一个目标文件

2.3创建test文件去调用我们的库

#import <Foundation/Foundation.h>
#import <ReplaceAFNetWorking.h>

int main(){
    ReplaceAFNetWorking *manager = [ReplaceAFNetWorking new];
    NSLog(@"testApp----%@", manager);
    return 0;
}

2.3把我们的test文件编译成目标文件(.o文件)

我们用clang编译我们的m文件成目标文件

man clang 
clang - the Clang C, C++, and Objective-C compiler
SYNOPSIS
       clang [options] filename ...

DESCRIPTION
       clang  is  a C, C++, and Objective-C compiler which encompasses prepro-
       cessing, parsing, optimization, code generation, assembly, and linking.
       Depending  on  which high-level mode setting is passed, Clang will stop
       before doing a full link.  While Clang  is  highly  integrated,  it  is
       important to understand the stages of compilation, to understand how to
       invoke it.  These stages are:

使用如下命令

clang -x objective-c \
-target x86_64-apple-macos11.0.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
-I./ReplaceAFNetWorking \
-c test.m -o test.o

2.4.生成可执行文件

clang -target x86_64-apple-macos11.0.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
-L./ReplaceAFNetWorking \
-lReplaceAFNetWorking \
test.o -o test

clang命令参数:
-x: 指定编译文件语言类型
-g: 生成调试信息
-c: 生成目标文件,只运行preprocess,compile,assemble,不链接
-o: 输出文件
-isysroot: 使用的SDK路径
1. -I<directory> 在指定目录寻找头文件 header search path
2. -L<dir> 指定库文件路径(.a.dylib库文件) library search path
3. -l<library_name> 指定链接的库文件名称(.a.dylib库文件)other link flags -lAFNetworking
-F<directory> 在指定目录寻找framework framework search path
-framework <framework_name> 指定链接的framework名称 other link flags -framework AFNetworking

3静态库原理

3.1探究的环境。

image.png
//TestExample的h文件
#import <Foundation/Foundation.h>

@interface TestExample : NSObject

- (void)lg_test:(_Nullable id)e;

@end
//TestExample的m文件
@implementation TestExample

- (void)lg_test:(_Nullable id)e {
    NSLog(@"TestExample----");
}
@end
//test文件的内容
#import <Foundation/Foundation.h>
#import "TestExample.h"

int main(){
    NSLog(@"testApp----");
   return 0;
}

3.2. TestExample文件编译成目标文件

 clang -x objective-c \
-target x86_64-apple-macos11.0.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
-c TestExample.m -o TestExample.o

3.3修改目标文件为库文件

把TestExample.o文件修改成libTestExample.a。如果系统比较新可以把o文件改成后缀dylib的文件

3.4编译test.m文件为目标文件

 clang -x objective-c \
-target x86_64-apple-macos11.0.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
-I./StaticLibrary \
> -c test.m -o test.o

3.5链接

clang \
-target x86_64-apple-macos11.0.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
-L./StaticLibrary \
-lTestExample \
test.o -o test

3.6执行

lldb
file test
//Current executable set to '/Users/MacW/Desktop/loginlearn/强化版/强化班-3-静 
//态库/上课代码/静态库原理/test' (x86_64).
r
//Process 14790 launched: '/Users/MacW/Desktop/loginlearn/强化版/强化班-3-静
//态库/上课代码/静态库原理/test' (x86_64)
//2021-01-21 19:19:24.535476+0800 test[14790:1474339] testApp----
//2021-01-21 19:19:24.535943+0800 test[14790:1474339] TestExample----
//Process 14790 exited with status = 0 (0x00000000)

总结,静态库就是目标文件.o文件的合集,Framework的合成和.a差不多,唯一区别就是在链接的时候-L和-l换成-F 和-framework

4.静态库合并

可以通过ar命令,
ar`压缩目标文件,并对其进行编号和索引,形成静态库。同时也可以解压缩静态库,查看有哪些目标文件:
ar -rc a.a a.o
-r: 像a.a添加or替换文件
-c: 不输出任何信息
-t: 列出包含的目标文件
ar -rc libTestExample.a TestExample.o
我们一般使用xcode给我提供的libtool

libtool \
 -static \
 -o libmerge.a \
 libSDWebImage.a \
 libAFNetworking.a

5.dead code strip

我们在3的探究库原理的时候,只引用了TestExample头文件,但是没有对TestExample使用,那TestExample代码是否被链接到我们的test文件中的呢

objdump --macho -d test
test:
(__TEXT,__text) section
_main:
100003f60:  55  pushq   %rbp
100003f61:  48 89 e5    movq    %rsp, %rbp
100003f64:  48 83 ec 10 subq    $16, %rsp
100003f68:  48 8d 05 99 00 00 00    leaq    153(%rip), %rax ## Objc cfstring ref: @"testApp----"
100003f6f:  c7 45 fc 00 00 00 00    movl    $0, -4(%rbp)
100003f76:  48 89 c7    movq    %rax, %rdi
100003f79:  b0 00   movb    $0, %al
100003f7b:  e8 08 00 00 00  callq   0x100003f88 ## symbol stub for: _NSLog
100003f80:  31 c0   xorl    %eax, %eax
100003f82:  48 83 c4 10 addq    $16, %rsp
100003f86:  5d  popq    %rbp
100003f87:  c3  retq

从命令中看出TestExample的代码并没有
我们在main中做如下修改

int main(){
    NSLog(@"testApp----");
    TestExample *manager = [TestExample new];
    [manager lg_test: nil];
    return 0;
}

编译连接后查看

100003ef9:  48 8b 35 c8 41 00 00    movq    16840(%rip), %rsi ## Objc selector ref: lg_test:

这样会出现一个问题,因为我们的分类是在运行时创建的,但是我们dead code是在连接的时候生效的,所以我们的分类就会被strip掉,当我们运行时就会出现实例找不到方法的情况。我们配置other linkers flags
我们在config文件配置如下

OTHER_LDFLAGS=-Xlinker -all_load

-Xlinker -all_load:不dead strip,加载全部代码
-Xlinker -ObjC:加载全部OC相关代码,包括分类
-Xlinker -force_load: 要加载那个静态库的全部代码
dead code strip和-all_load的区别
-all_load 指定对静态库的链接情况,-ObjC 是全部链接还是只链接oc符号, -force_load是指定链接某一个静态库。和-dead_strip的作用如下

-dead_strip
                 Remove functions and data that are unreachable by the entry
                 point or exported symbols.

我们在实际应用中,可以如下指定参数

clang -target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot $SYSROOT \
-Xlinker -dead_strip \
-Xlinker -all_load \
-Xlinker -why_live -Xlinker _global_function \
-L./StaticLibrary \
-lTestExample \
${FILE_NAME}.o -o ${FILE_NAME}

-why_live -Xlinker:指某个符号为什么存在

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容