静态库原理

689 阅读5分钟

库(Library)就是一段编译好二进制代码,加上头文件就可以供别人使用 库文件格式

  • .a:常见的静态库
  • .dylib:传统上说的动态库
  • .framework:既有动态库,又有静态库
  • .xcframework:是苹果2018年推出来的,可以将不同架构库整合到一起。好处就是模拟器,真机可以通用,上架AppStore,不需要将xcframework中的真机架构分离.framework还需要用脚本分离

库的使用场景

  • 某些代码需要给别人使用,但是我们不希望别人看到源码,就需要以库的形式进行封装,只`暴露出头文件。
  • 对于某些不会进行大的改动的代码,我们想减少编译的时间,就可以把它打包成库,因为库是已经编译好的二进制了,编译的时候只需要Link一下不会浪费编译时间

静态库与动态库

静态库

静态库即静态链接库:可以简单的看成一组目标文件的集合。即很多目标文件经过压缩打包后形成的文件。Windows下的 .libLinux 和 Mac 下的 .aMac独有的.framework

  • 缺点: 浪费内存磁盘空间模块更新困难

动态库

与静态库相反,动态库编译时不会拷⻉到目标程序中,目标程序中只会存储指向动态库的引用。等到程序运行时动态库才会被真正加载进来。格有:.framework、.dylib、.tdb

  • 缺点: 会导致一些性能损失。但是可以优化,比如延迟绑定(Lazy Binding)技术

静态库生成

生成.o文件

编译过程首先就是要生成目标文件(也就是.o文件),之后通过链接器生成可执行文件 所以将我们最上面的.m文件生成.o文件,用到我们经常使用的clang命令, clang就是C、C++和OC的编译器


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

命令解释

  • 1.-x制定语言/转译字符,输入后告诉终端敲回车是换行,不是执行
  • 2.制定平台
  • 3.编译成arc环境
  • 4.指定使用的SDK路径
  • 5.I 在指定目录寻找头文件AFN 如果没有引用到外部库就不加
  • 6.将.m编译成.o

回车就生成了.o文件

生成静态库

将生成的.o文件通过链接器生成静态库


clang -target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk \
-L./AFNetworking \
-lAFNetworking \
test.o -o test

  • 1.这里不需要制定语言,直接选择架构
  • 2.选择arc环境
  • 3.由于项目中有NSLog,其导出符号,要知道其来源自Foundation,让他去我们指定的Xcode中寻找
  • 4.指定目录寻找头文件(链接的过程就是把我们重定位符号表中的符号进行重定位,这里就需要知道符号的真实位置,AFN的符号表放在了libAFNetworking.a文件里了,它需要和我们生成的test.o文件的符号表进行融合,最后生成一张新的符号表
  • 5.指定连接的库文件名称
  • 6.将.o文件输出为test文件

有个细节注意架构问题,如果引用不同架构,导出的时候会报错 x86架构

ar生成.a静态库

1.在OCStaticLib文件夹中准备OCTest.hOCTest.m两个简单的类文件

2.将OCTest.m编译成.o目标文件

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

3.将OCTest.o文件生成.a静态库

//生成 .a 文件
ar -rc libOCTest.a OCTest.o
// 查看包含的目标文件
ar -t +Path

  • ar 参数说明

    • -r:向.a中添加或替换文件(无则添加, 有则替换)
    • -c:不输出任何信息
    • -t:列出包含的目标文件

静态库合并

1.静态库是.o文件的合集, 静态库合并就是将静态库的.o合并, 可以实现的工具

  • ar, 静态库的查看、拆分、合并
  • libtoolxcode提供的静态库操作工具

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

  • 参数说明

    • libMerge.a是合并后生成静态库的名称
    • libAFNetworking.alibSDWebImage.a是将要被合并的两个静态库
  • 利用ar来验证下静态库文件是否包含两个静态库的.o文件

 ar -t /Users/shenyj/Documents/CodeForTest/staticLibMerge/staticLib/libMerge.a

制作脚本


LANGUAGE=objective-c

TAREGT=x86_64-apple-macos11.3

SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk


FILE_NAME=test

STATICLIBRARY=TestExample

HEAD_PATH=./StaticLibrary

LIBRARY_PATH=./StaticLibrary


echo "-------------编译test.m to test.o------------------"

clang -x $LANGUAGE  \

-target $TAREGT     \

-fobjc-arc          \

-isysroot $SYSROOT  \

-I${HEAD_PATH}   \

-c ${FILE_NAME}.m -o ${FILE_NAME}.o


echo "-------------进入到StaticLibrary目录------------------"

pushd ${HEAD_PATH}

echo "-------------编译TestExample.m to TestExample.o------------------"

clang -x $LANGUAGE  \

-target $TAREGT     \

-fobjc-arc          \

-isysroot $SYSROOT  \

-c ${STATICLIBRARY}.m -o ${STATICLIBRARY}.o

echo "-------------退出StaticLibrary目录------------------"


popd


echo "-------------test.o链接libTestExample.a to test EXEC------------------"

clang -target $TAREGT   \

-fobjc-arc              \

-isysroot $SYSROOT      \

-L${LIBRARY_PATH}       \

-l${STATICLIBRARY}           \

$FILE_NAME.o -o $FILE_NAME


权限相关

chmod +x ./build.sh

./build.sh

lldb

file test

r

静态库优化

1.通过xcconfig来取代build settings设置

// Xlinker 作用告诉 clang 参数传递给 lldb 使用

// -Xlinker -all_load:不dead strip,加载全部代码

// -Xlinker -ObjC:加载全部OC相关代码,包括分类

// -force_load: 要加载那个静态库的全部代码

LGSTATIC_FRAMEWORK_PATH=${BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/LGStaticFramework.framework/LGStaticFramework

OTHER_LDFLAGS=-Xlinker -force_load ${LGSTATIC_FRAMEWORK_PATH}

  • 其中Xlinker的作用

    • OTHER_LDFLAGS通过clangld传递参数 
    • 并不是给clang的参数, clang没有这些参数(-ObjC…), 通过 Xlinker告诉clang传递给ld

重点 LTO Link-Time Optimization, dead strip之后执行的 优化代码体积。暂不陈述,以后会补充

总结

生成静态库的三要素:

  • -I-I<directory> 在指定目录寻找头文件, 等同于Build Setting设置header search path
  • -L-L<dir> 指定库文件路径(.a.dylib库文件, 等同于Build Setting设置library search path
  • -l-l<library_name> 指定链接的库文件名称(.a.dylib库文件), 等同于Build Setting设置other link flags -lAFNetworking

补充

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