问题
先列举一下本文要叙述的问题~解答后会经过演示证明
- 1、什么是动态库?
- 2、什么是动态库?
- 3、动态库和静态库的差异是什么?
- 4、动态库、静态库、
framework
是什么联系? - 5、动态库和静态库链接到主程序以后放在什么位置?
- 6、什么是
dead strip
? - 7、
-all_load、-noall_load、-ObjC、-force_load
参数的差异? - 8、什么是
tbd
文件? - 9、动态库和静态库的挑选?
- 10、为什么项目里动态库不能超过六个?
- 11、怎样剥离动态库里不需求的架构?
1、什么是静态库?
静态库是静态链接库;是多个方针文件经过压缩打包后构成的文件包。以下都是静态库的类型
- Windows 的
.lib
- Linux 的
.a
- MacOS 独有的
.framework
2、什么是动态库?
- 动态库是动态链接库,是实现同享函数库的一种办法。
- 动态库在编译的时分不会被拷贝到方针程序中,方针程序只会存储下动态库的引证。
- 真正用到动态库内的函数时才会去查找 – 绑定 – 运用函数。
- 动态库的格局有:
.framework
、.dylib
、.tbd
……
3、动态库和静态库的差异
- 静态库
- 在编译时加载
- 优点:代码装载和履行速度比动态库快。
- 缺点:糟蹋内存和磁盘空间,模块更新困难。
- 动态库
- 在运转时加载
- 优点:体积比静态库小许多,愈加节省内存。
- 缺点:代码装载和履行速度比静态库慢。
- 备注:
- 体积小于
最小单位16k
的静态库编译出来的动态库体积会等于16k
。 - 换成动态库会导致⼀些速度变低,可是会经过推迟绑定(
Lazy Binding
)技能优化。 - 推迟绑定:首次运用的时分查找并记载办法的内存地址,后续调用就能够省掉查找流程。
- 体积小于
4、动态库、静态库、framework是什么联系?
- 库是现已编译完结的二进制文件。
- 代码需求供给给外部运用又不想代码被更改,就能够把代码封装成库,只露出头文件以供调用。
- 期望进步编译速度,能够把部分代码封装成库,编译时只需求链接。
- 库都是需求链接的,链接库的办法有静态和动态,所以就产生了静态库和动态库。
framework
其实是一种文件的打包办法,把头文件、二进制文件、资源文件封装在一起,便利管理和分发。所以动态库和静态库的文件格局都会有.framework
。
-
Dynamic Framework
动态库,系统供给的framework
都是动态库。比方UIKit.framework
,具有一切动态库的特性。 -
Static Framework
静态库,开发者能够制造。能够了解为静态库 = 头文件 + 资源文件 + 二进制代码
,具有静态库的特点。 -
Embedded Framework
也是动态库的一种,用户能够制造。系统的Framework
不需求拷贝到方针程序中,Embedded Framework
终究需求拷贝到APP中。他具有部分动态特性,能够在Extension可履行文件
和方针APP
之间同享。 -
XCFramework
是苹果官⽅推荐的、⽀持的文件格局。支撑 xcode11 以上,能够供给多个不同渠道的分发二进制文件,xcode会自动判别你需求编译的ipa
包是什么架构,运用的时分就不必经过脚本剥离不需求的架构系统。
5、动态库和静态库链接到主程序以后放在什么位置?
6、什么是dead strip
?
dead strip
能够在编译时把没有用到的代码屏蔽在外,以节约包体积。
7、-all_load
、-noall_load
、-ObjC
、-force_load
参数的差异?
这几个参数只对链接静态库收效
-
-all_load
:加载悉数代码 -
-noall_load
:默许参数,屏蔽未用代码 -
-ObjC
:加载悉数OC
相关代码,包含分类 -
-force_load
: 能够加载指定静态库的悉数代码
8、什么是tbd
文件?
-
tbd
全称是txt-based stub libraries
,本质上是一个YMAL
描述文本文件。 - 用于记载动态库信息,包含 导出符号、动态库框架信息、动态库依靠信息。
- 真机情况下动态库都在手机内
-
xcode
开发时相关的库存在MacOS
,不必存储Xcode
内。运用tbd
格局的伪framework
能够大大减少xcode
的大小。
9、动态库和静态库的挑选?
- 相同代码打包成动态库比静态库体积更小
- 静态库是
.o
文件的合集,每个.o
都包含大局符号,多个.o
会重复包含大局符号,库体积更大。 - 动态库是编译链接产品,一切符号都放在一起,大局符号只存1次,库体积更小。
- 静态库是
- 运用静态库的工程生成
ipa
包体积更小- 动态库是编译链接的终究产品,无法优化,需求拷贝到
frameworks
文件夹中,会添加ipa
包体积。 - 工程编译默许将静态库代码合并到
APP主程序符号表
,.framework
格局静态库不含资源文件的时分能够挑选Do not embed
,这样静态库文件不会嵌入包,能够缩小安装包体积 - 静态库还能够经过设置
-noall_load
、-ObjC
、-force_load
屏蔽不需求的代码。
- 动态库是编译链接的终究产品,无法优化,需求拷贝到
10、为什么项目里动态库不能超过六个?
由于动态库是APP
运转时动态加载的,数量多了会影响发动速度。
11、怎样剥离动态库里不需求的架构?
- 1、编译库文件
- 2、
lipo
指令剥离不需求的架构 - 3、从头拖到项目
演示
接下来手把手经过Xcode
和模仿脚本模仿打包过程并验证上述的问题。
1、创立项目
首先在同一目录创立一个主工程APP,一个静态库,一个动态库
在主工程APP内创立Workspace
封闭主APP工程,翻开workspace添加两个库工程
添加完结后的工程文件目录如下
2、库工程的设置
封闭workspace,翻开库工程(两个设置都相同)
Project -> BuildSettings -> Deployment Postprocessing
设置为YES
这个设置相当于 Deployment
一整列的开关,所以一定要翻开
并不是一切的符号都是有必要的,比方 Debug Map
,所以 Xcode
供给了Strip Linked Product
去除不需求的符号信息,能够经过设置 Strip Style
参数控制效果。
稍后会演示这个参数怎样运用。
去除符号信息后只能运用 dSYM
进行符号化,所以需求将 Debug Information Format
修改为 DWARF with dSYM file
。
ps:没有 DWARF 调试信息
,Xcode
靠什么来生成dSYM
?
仍是经过 DWARF
,Xcode
的编译步骤:
- 生成带有
DWARF
调试信息的可履行文件 - 提取可履行文件中的信息打包成
dSYM
- 运用 strip 指令去除符号化信息
Project -> BuildSettings -> Strip Style
设置为 Debugging Symbols(默许)
挑选不同的 Strip Style
,APP
构建终究一步的 Strip操作
会带上对应参数。
挑选 debugging symbols
在函数调用栈中,能够看到类名和办法名。
添加能够露出的头文件
3、生成XCConfig
装备文件
先创立一个装备文件debugConfig.cxconfig
,里边指定库以及主程序途径
指定运用装备文件
4、初步运转工程
编译成功后,能够看到产品里边会呈现动态库.framework
及静态库.a
文件
5、装备运转脚本
接下来,给xcode
添加一个脚本,使得编译的时分能把符号表输出到终端。
在主程序的根目录下创立一个名为xcode_run_cmd.sh
的脚本文件
代码直接贴出来便利大家cv操作。
#!/bin/sh
RunCommand() {
#判别大局字符串VERBOSE_SCRIPT_LOGGING是否为空。-n string判别字符串是否非空
#[[是 bash 程序语言的关键字。用于判别
if [[ -n "$VERBOSE_SCRIPT_LOGGING" ]]; then
#作为一个字符串输出一切参数。运用时加引号"$*" 会将一切的参数作为一个整体,以"$1 $2 … $n"的方法输出一切参数
if [[ -n "$TTY" ]]; then
echo "♦ $@" 1>$TTY
else
echo "♦ $*"
fi
echo "------------------------------------------------------------------------------" 1>$TTY
fi
#与$*相同。可是运用时加引号,并在引号中回来每个参数。"$@" 会将各个参数分开,以"$1" "$2" … "$n" 的方法输出一切参数
if [[ -n "$TTY" ]]; then
eval "$@" &>$TTY
else
"/bin/bash $@"
fi
#显示终究指令的退出状况。0标明没有过错,其他任何值标明有过错。
return $?
}
EchoError() {
#在shell脚本中,默许情况下,总是有三个文件处于翻开状况,规范输入(键盘输入)、规范输出(输出到屏幕)、规范过错(也是输出到屏幕),它们别离对应的文件描述符是0,1,2
# > 默许为规范输出重定向,与 1> 相同
# 2>&1 意思是把 规范过错输出 重定向到 规范输出.
# &>file 意思是把规范输出 和 规范过错输出 都重定向到文件file中
# 1>&2 将规范输出重定向到规范过错输出。实际上便是打印一切参数已规范过错格局
if [[ -n "$TTY" ]]; then
echo "$@" 1>&2>$TTY
else
echo "$@" 1>&2
fi
}
RunCMDToTTY() {
if [[ ! -e "$TTY" ]]; then
EchoError "=========================================="
EchoError "ERROR: Not Config tty to output."
exit -1
fi
# CMD = 运转到指令
# CMD_FLAG = 运转到指令参数
# TTY = 终端
if [[ -n "$CMD" ]]; then
RunCommand $CMD
else
EchoError "=========================================="
EchoError "ERROR:Failed to run CMD. THE CMD must not null"
fi
}
RunCMDToTTY
主程序里装备脚本途径 Target -> Build Phases -> Run TTY Script
添加
/bin/bash -c "${SRCROOT}/xcode_run_cmd.sh"
6、创立运转脚本的装备文件
翻开终端,输入tty
,看到一个途径,记下来且不要封闭终端
创立一个名为runConfig.xcconfig
的XCConfig
文件,而且添加如下代码
记住上面第一次创立的装备文件吗,由于XCode只支撑一份装备文件,所以不需求改动装备途径
直接在第一份装备里引证runConfig.xcconfig
即可
#include "runConfig.xcconfig"
运转工程,接下来检查终端,现已输出的咱们的指令~
7、输出符号信息
在runConfig.xcconfig
里边添加检查符号表的指令
括号内称号便是debugConfig.xcconfig
里装备的
// nm 检查符号表
// p:不分类 a:检查一切machO符号,了解成all
// ${地址}
CMD = nm -pa ${MYSTATICLIB_PATH}
在静态库内添加一个测验办法
再次运转代码能够在终端里看到,测验办法的符号
为了测验的便利和明晰,动态库和静态库都各添加上两个类
- 一个揭露类
PublibObj
- 一个私有类
PrivateObj
都添加上Test
办法
此刻输出动态库的符号表,能够看到不同类的办法是放在一起的
再输出静态库的符号表,看到符号表是依照.o
文件分类输出的
这也能够印证静态库便是一个个.o
文件的合集
终究输出主工程的符号表
8、运用动态库和静态库
主工程内创立一个文件夹并添加两个揭露类的引证,调用两个测验办法
再次检查终端,搜索静态库的办法,能查到其符号
再去查找动态库办法时,却发现找不到他的符号
其实上述现象正好印证了动态库和静态库的加载时期并不相同,脚本是在编译时运转的
-
静态库的办法也是在编译期加载,所以这儿能获取到静态库的符号。
-
动态库的办法则是在运转时加载,所以脚本运转时获取不到动态库的符号。
9、检查静态库的代码
经过objdump
指令能够检查具体的代码情况
// objdump 检查
// -macho 检查macho格局的信息
// -d 打印代码块的内容
// ${地址}
CMD = objdump -macho -d ${APP_PATH}
能够看到动态库的代码只有不幸的两行,这儿其实是ViewDidLoad
在调用
可是动态库的代码除了被调用以外,连实现都在这儿输出了
而且能楚的看到,静态库的代码和主程序的其他代码放在一块了~
这是由于静态库在编译时会仿制一份代码到大局符号表~
10、静态库的链接办法
前面现已叙述了下列几个参数只对链接静态库收效
-
-all_load
:加载悉数代码 -
-noall_load
:默许参数,屏蔽未用代码 -
-ObjC
:加载悉数OC
相关代码,包含分类 -
-force_load
: 能够加载指定静态库的悉数代码
下面来稍稍验证一下~
把ViewContrller
的代码屏蔽
运转后在终端是无法找到任何与静态库相关的内容,由于主工程是默许敞开dead strip
Target -> Building Setting -> Other Linker Flag
设置为 -all_load
再次运转
从头搜索到staticPublicTest
的实现,这就证明了 -all_load
能够加载悉数代码
接下来把ViewContrller
的代码屏蔽放开
Target -> Building Setting -> Other Linker Flag
设置为 -noall_load
再次运转
此刻报错无法运转。当然了,一切符号都不加载程序必定没法跑
-ObjC
这个欠好演示,就稍微讲一讲吧~
他的作用是将静态库中任何Objective-C
代码都链接到APP中。
任何也就包含了Category
的办法,这就导致运用-ObjC
或许会链接许多静态库中未被运用的Objective-C
代码,极大的添加APP
的代码体积。
至于-force_load
和前面-all_load
的用法根本共同,只需求在参数后边添加静态库的地址即可,这样就加载指定静态库的悉数代码~
10、构建 XCFramework
并运用
在动态库工程的根目录创立一个脚本pack_xcframe.sh
,仿制下列代码
FREAMEWORK_NAME='MyDynamicLib'
修改为你的库名就能用了
#!/bin/sh -e
# Framework/工程 的名字
FREAMEWORK_NAME='MyDynamicLib'
# 一切产品的方针途径
OUTPUT_DIR="./Build/${FREAMEWORK_NAME}"
# Device Archive 生成的 .xcarchive 寄存途径。在工程的根目录下生成 Build 文件夹。
ARCHIVE_PATH_IOS_DEVICE="./${OUTPUT_DIR}/${FREAMEWORK_NAME}_device.xcarchive"
# Simulator Archive 生成的 .xcarchive 寄存途径。
ARCHIVE_PATH_IOS_SIMULATOR="./${OUTPUT_DIR}/${FREAMEWORK_NAME}_simulator.xcarchive"
# 制造完 framework 后,是否在 Finder 中翻开
REVEAL_XCFRAMEWORK_IN_FINDER=true
# 生成单个渠道的 .xcarchive. 接纳4个参数, scheme, destination, archivePath,指令集.
function archiveOnePlatform {
echo "▸ Starts archiving the scheme: ${1} for destination: ${2};\n▸ Archive path: ${3}"
xcodebuild archive \
-scheme "${1}" \
-destination "${2}" \
-archivePath "${3}" \
VALID_ARCHS="${4}" \
SKIP_INSTALL=NO\
BUILD_LIBRARY_FOR_DISTRIBUTION=YES
}
function archiveAllPlatforms {
# Platform Destination
# iOS generic/platform=iOS
# iOS Simulator generic/platform=iOS Simulator
# iPadOS generic/platform=iPadOS
# iPadOS Simulator generic/platform=iPadOS Simulator
# macOS generic/platform=macOS
# tvOS generic/platform=tvOS
# watchOS generic/platform=watchOS
# watchOS Simulator generic/platform=watchOS Simulator
# carPlayOS generic/platform=carPlayOS
# carPlayOS Simulator generic/platform=carPlayOS Simulator
SCHEME=${1}
archiveOnePlatform $SCHEME "generic/platform=iOS Simulator" ${ARCHIVE_PATH_IOS_SIMULATOR} "x86_64"
archiveOnePlatform $SCHEME "generic/platform=iOS" ${ARCHIVE_PATH_IOS_DEVICE} "armv7 arm64"
}
function makeXCFramework {
mkdir -p "${OUTPUT_DIR}"
FRAMEWORK_RELATIVE_PATH="Products/Library/Frameworks"
sudo xcodebuild -create-xcframework \
-framework "${ARCHIVE_PATH_IOS_DEVICE}/${FRAMEWORK_RELATIVE_PATH}/${FREAMEWORK_NAME}.framework" \
-framework "${ARCHIVE_PATH_IOS_SIMULATOR}/${FRAMEWORK_RELATIVE_PATH}/${FREAMEWORK_NAME}.framework" \
-output "${OUTPUT_DIR}/${FREAMEWORK_NAME}.xcframework"
}
echo "##################### 发动脚本 #####################"
echo "##################### 重置方针途径 ${OUTPUT_DIR} #####################"
sudo rm -rf $OUTPUT_DIR
echo "##################### 正在归档 ${FREAMEWORK_NAME} #####################"
archiveAllPlatforms $FREAMEWORK_NAME
echo "##################### 正在制造 framework: ${FREAMEWORK_NAME}.xcframework #####################"
makeXCFramework
if [ ${REVEAL_XCFRAMEWORK_IN_FINDER} = true ]; then
sudo open "${OUTPUT_DIR}/"
fi
终端调用一下
创立成功~
怎样运用?
- 1、跟一般的动态库相同~
XCFramework
仿制到工程里边 - 2、
Target -> General -> Frameworks,Libraries,and Embeddded Content
添加一下 - 3、然后
import
头文件调用就行
总结瞎掰
总结其实也没啥能总了…
便是最近沸点吐槽水文闹的沸反盈天…
这儿纯瞎说一下
这篇东西3天熬夜时断时续搞出来的,查资料、xcode调试、截图、码字要老命了…
质量嘛,自我感觉还行,纯手打真手把手教程,小白跟着也能走完整个过程
可是也有或许被以为是水文,毕竟没有很深奥的东西
其实吧
像我记性欠好的人,喜欢记载一下折腾过的东西和踩过的坑
以后用到的时分翻一下自己的主页就找到,也不必去搜,便利省时间
万一有相同问题的朋友能搜到看到,那帮到人就更开心~
技能社区嘛,技能的东西多容纳~