iOS动态库与静态库的理解与创建

一、动态库与静态库简介

1.1、什么是库

库(Library)是一个可供使用的各种标准程序、子程序、文件以及它们的目录等信息的有序集合。

所谓程序库,一般来说就是软件作者为了实现发布方便或替换方便又或者是二次发布方便这个目的,所制作的一组可以单独与应用程序进行静态链接或动态链接的二进制可重定位目标码文件。
说到底,一个库就是一个文件而已。这个库(文件)可以是在编译时由编译器直接链接到可执行程序之中,也可以在运行时根据操作系统的运行环境按需要动态加载到内存之中。再简单点,我们可以认为库(文件)就是一个代码仓库,里面有一些我们可以直接拿来用的变量,函数或者类。

在Mac的平台下,动态库以 .dylib 或者 .framework 后缀结尾,静态库以 .a 和 .framework 结尾。在Linux的平台下,静态库文件的后缀是 .a ,动态库的后缀是 .so 。在window的平台下,静态库文件的后缀是 .lib ,动态库的后缀是.dll。

1.2、什么是静态库

静态库 (Static Libraries)即静态链接库,之所以叫做静态,是因为静态库在编译的时候会被直接拷贝一份,复制到目标程序里,这段代码在目标程序里就不会再改变了。也可以简单的理解是多个目标文件 (object file, 以 .o 为后缀) 的打包集合。

静态库的好处很明显,编译完成之后,库文件实际上就没有作用了。目标程序没有外部依赖,直接就可以运行。当然其缺点也很明显,就是会使用目标程序的体积增大。

图1:静态库与动态库.png

1.3、什么是动态库

动态库 (Dynamic Libraries)即动态链接库。跟静态库一样是多个 object files 封装起来的,但与静态库相反的是动态库在编译时并不会被拷贝到目标程序中,目标程序中只会存储指向动态库的引用。等到程序运行时,动态库才会被真正加载进来。

动态库的优点是,不需要拷贝到目标程序中,不会影响目标程序的体积,而且同一份库可以被多个程序使用(因为这个原因,动态库也被称作共享库)。同时,运行时才载入的特性,也可以让我们随时对库进行替换,而不需要重新编译代码。动态库带来的问题主要是,动态载入会带来一部分性能损失,使用动态库也会使得程序依赖于外部环境。如果环境缺少动态库或者库的版本不正确,就会导致程序无法运行(Linux 下喜闻乐见的 lib not found 错误)。

1.4、静态库与动态库各自优点

静态库优点:

  • 模块化,分工合作,提高了代码的复用及核心技术的保密程度;
  • 避免少量改动经常导致大量的重复编译连接;
  • 也可以重用,注意不是共享使用。

动态库优点:

  • 可以将最终可执行文件体积缩小,将整个应用程序分模块,团队合作,进行分工,影响比较小;
  • 多个应用程序共享内存中得同一份库文件,节省资源;
  • 可以不重新编译连接可执行程序的前提下,更新动态库文件达到更新应用程序的目的;
  • 应用插件化;
  • 软件版本实时模块升级;
  • 在其它大部分平台上,动态库都可以用于不同应用间共享, 共享可执行文件,这就大大节省了内存。

在 iOS8 之前,苹果不允许第三方框架使用动态方式加载,从 iOS8 开始允许开发者有条件地创建和使用动态框架,这种框架叫做 Cocoa Touch Framework。虽然同样是动态框架,但是和系统 framework 不同,苹果系统专属的 framework 是共享的(如 UIKit),使用 Cocoa Touch Framework 制作的动态库在打包和提交 App 时会被放到 App main bundle 的根目录中,运行在沙盒里,而不是系统中。也就是说,不同的 App 就算使用了同样的 framework,但还是会有多份的框架被分别签名、打包和加载。不过 iOS8 上开放了 App Extension 功能,可以为一个应用创建插件,这样主 App 和插件之间共享动态库还是可行的。

二、静态库的创建

可下载 Demo

2.1 创建静态库工程

Xcode -> Create a new Xcode project -> iOS -> Static Library

图2:创建静态库工程.png

2.2 暴露头文件(.h)以供SDK使用者调用

首先我们新建两个测试类ZJHStaticPublicTool、ZJHStaticPrivateTool,一个公有,一个私有。Bulid Phases -> Copy Files -> +将需要开放的头文件,开放出去。

图3:静态库暴露头文件.png

2.3 编译与查看

之后配置一下运行环境,选择模拟器还是真机,然后是Debug和Release环境选择。

图4:静态库编译配置.png

配置完,带你“run”,开始编译,编译的结果,可以在Product->Show Bulid Folder in Finder -> Products 中获取对应的SDK。拖入你自己的工程项目就能正常运行啦。

2.4 显示Products文件夹

Xcode13后默认不显示“Products”,也可以设置下,让它显示出来。首先打开项目,然后进入到你的项目目录并打开project.pbxproj文件。 1)show in Finder 找到项目在电脑上的位置;2)右键点击xxx.xcodeproj -> 选择显示包内容;3)右键点击project.pbxproj -> 选择Xcode等工具打开文件。搜索productRefGroup 关键字,搜索结果可能有多个,每个项目的键值不一样具体看自己的项目。注意看productRefGroup的注释 为/* Products */ 才是我们要修改的。将mainGroup的值赋值给productRefGroup,之后保存 project.pbxproj文件,Xcode将自动刷新,这时候你想见的 Products 目录就出现了。

图5:显示Products文件夹.png

三、动态库的创建

3.1 创建动态库工程

Xcode -> Create a new Xcode project -> iOS -> Framework

图6:创建动态库工程.png

3.2 暴露头文件(.h)以供SDK使用者调用

这里我们也新建两个测试类ZJHDynamicPublicTool、ZJHDynamicPrivateTool,一个公有,一个私有。在Bulid Phases 中找到Headers目录,将需要暴露的头文件拖到public目录下。

图7:动态库暴露头文件.png

之后配置一下运行环境,选择模拟器还是真机,然后是Debug和Release环境选择。配置完,带你“run”,开始编译,编译的结果,可以在Product->Show Bulid Folder in Finder -> Products 中获取对应的SDK。

然后我们看项目中的TARGETS->KGFramework->Build Settings->Mach-O Type,系统默认为我们设置的是Dynamic Library动态库,如果我们想改为静态库的话,直接在这里手动修改为Static Library即我们静态库。

拖入项目工程运行后,这时候你会发现报错了Reason: image not found,遇到问题不要慌,我们来看下,程序运行时,动态库由系统动态加载到内存中,这些动态库是以镜像的文件存在的,所以在这里报错image没找到是很正常的事情,我们到TARGETS选择当前项目,然后点击Frameworks,Libraries,and Embedded Content然后选择我们的动态库,设置它的Embed为Embed & Sign,然后重新编译运行。你会发现成功打印我们动态库测试方法内的内容。

图8:动态库运行报错修改.png

3.3 使用Shell打包Framework

3.3.1 新建Shell专用Target

新建Shell专用Target,我这里命名为 ZJHDynamicShell

图9:新建Shell专用Target.png
3.3.2 添加脚本Phase
图10:添加脚本Phase.png

我写的脚本如下,大家可以根据自己项目的实际情况修改脚本文件

#!/bin/sh
#要build的target名
TARGET_NAME="ZJHDynamicSDK"

# 版本号
buildNumber="1.0.0"

# 输出到桌面文件路径名
Desktop="${HOME}/Desktop/sdk_pack"
UNIVERSAL_OUTPUT_FOLDER="${Desktop}/sdk_v$buildNumber/"
#创建输出目录,并删除之前的framework文件
mkdir -p "${UNIVERSAL_OUTPUT_FOLDER}"
rm -rf "${UNIVERSAL_OUTPUT_FOLDER}/${TARGET_NAME}.framework"

#分别编译模拟器和真机的Framework
#xcodebuild -target "${TARGET_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
# 只编译模拟器的Framework
# 1、选择target名
# 2、ONLY_ACTIVE_ARCH 设置为NO
# 3、configuration 选择当前配置是release还是debug,可以在“Edit Scheme”中配置
xcodebuild -target "${TARGET_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphonesimulator BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build

#拷贝framework到univer目录
cp -R "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${TARGET_NAME}.framework" "${UNIVERSAL_OUTPUT_FOLDER}"

#合并framework,输出最终的framework到build目录
# lipo -create -output "${UNIVERSAL_OUTPUT_FOLDER}/${TARGET_NAME}.framework/${TARGET_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${TARGET_NAME}.framework/${TARGET_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${TARGET_NAME}.framework/${TARGET_NAME}"

#删除编译之后生成的无关的配置文件
dir_path="${UNIVERSAL_OUTPUT_FOLDER}/${TARGET_NAME}.framework/"
for file in ls $dir_path
do
if [[ ${file} =~ ".xcconfig" ]]
then
rm -f "${dir_path}/${file}"
fi
done
#判断build文件夹是否存在,存在则删除
if [ -d "${SRCROOT}/build" ]
then
rm -rf "${SRCROOT}/build"
fi
rm -rf "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator" "${BUILD_DIR}/${CONFIGURATION}-iphoneos"
#打开合并后的文件夹
open "${UNIVERSAL_OUTPUT_FOLDER}"

3.3.3 运行Shell打包

根据实际情况,修改自己的配置,之后点击“run”运行脚本,然后在桌面,就能得到自己的包啦

图11:修改脚本配置然后运行.png

四、调试动态库与静态库

开发库时,库工程中是不能直接运行看效果的,我们需要导入demo运行查看效果。但是每次更改f库代码,就得打包再导入测试项目中看实际效果,很麻烦且效率低。这里我们可以使用工程联调来解决这一问题,采用workspace的方式,多个工程集成到一个工作空间。

4.1、创建demo及Workspace

首页随便创建一个demo工程即可,我这边创建了ZJHAppDemo,之后新建 File->New->Workspace,也命名ZJHAppDemo,和ZJHAppDemo存放在统一目录。

图12:新建workspace.png

4.2、将库项目添加到Workspace

打开创建的workspace,工程中File->Add File To workspace,通过Add的方式将demo工程和两个库工程都添加进去。

图13:将库项目添加到Workspace.png

4.3、将库项目和调式demo关联起来

图14:将库项目和调式demo关联起来.png

这样就将工程们关联起来了,后续开发中就可以边在库项目中修改核心代码,边在demo中运行查看效果了。

图15:查看实时修改运行效果.png

五、库的合并与类型查看

5.1、合并 .a 格式的静态库

一般静态库中支持真机和模拟器多种CPU架构,比如(armv6, armv7,armv7s, arm64, i386, x86_64)。

使用下面指令查看当前静态库包含的架构的信息。

lipo -info xxx.a

使用以下命令可以实现将xxx.a中的 arm64 架构分离为新的 xxx_arm64.a 静态库(该静态库只包含arm64架构)。

lipo ./xxx.a -thin arm64 -output xxx_arm64.a

使用下面命令可实现将xxx_armv7.a与xxx_arm64.a合并为一个新的静态库new.a.

lipo -create xxx_armv7.a xxx_arm64.a -output new.a
图16:合并 .a 格式的静态库.png

5.2、合并 .framework 格式的库(静态库或者动态库)

.framework合并方法和.a合并方法相同,只不过.framework合并的是.framework内同名的那个文件。示例如下

lipo -create ZJHDynamicSDK_arm64 ZJHDynamicSDK_x86 -output ZJHDynamicSDK_standard

之后我们将合并得到的ZJHDynamicSDK_standard文件改回原来的名字(例如我这里应该改成ZJHDynamicSDK),替换任一.framework下的ZJHDynamicSDK文件, 该.framework就是我们最终需要的.framework文件了。

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

推荐阅读更多精彩内容