问题

先列举一下本文要叙述的问题~解答后会经过演示证明

  • 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

iOS 静态库动态库看这里

  • Dynamic Framework动态库,系统供给的 framework 都是动态库。比方 UIKit.framework,具有一切动态库的特性。

  • Static Framework静态库,开发者能够制造。能够了解为静态库 = 头文件 + 资源文件 + 二进制代码,具有静态库的特点。

  • Embedded Framework也是动态库的一种,用户能够制造。系统的Framework不需求拷贝到方针程序中,Embedded Framework终究需求拷贝到APP中。他具有部分动态特性,能够在 Extension可履行文件方针APP 之间同享。

  • XCFramework是苹果官⽅推荐的、⽀持的文件格局。支撑 xcode11 以上,能够供给多个不同渠道的分发二进制文件,xcode会自动判别你需求编译的ipa包是什么架构,运用的时分就不必经过脚本剥离不需求的架构系统。

5、动态库和静态库链接到主程序以后放在什么位置?

iOS 静态库动态库看这里

6、什么是dead strip

dead strip 能够在编译时把没有用到的代码屏蔽在外,以节约包体积。

iOS 静态库动态库看这里

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,一个静态库,一个动态库

iOS 静态库动态库看这里
iOS 静态库动态库看这里

在主工程APP内创立Workspace

iOS 静态库动态库看这里
iOS 静态库动态库看这里

封闭主APP工程,翻开workspace添加两个库工程

iOS 静态库动态库看这里
iOS 静态库动态库看这里
iOS 静态库动态库看这里
iOS 静态库动态库看这里

添加完结后的工程文件目录如下

iOS 静态库动态库看这里

2、库工程的设置

封闭workspace,翻开库工程(两个设置都相同)

Project -> BuildSettings -> Deployment Postprocessing 设置为YES

这个设置相当于 Deployment 一整列的开关,所以一定要翻开

iOS 静态库动态库看这里

并不是一切的符号都是有必要的,比方 Debug Map,所以 Xcode 供给了Strip Linked Product去除不需求的符号信息,能够经过设置 Strip Style 参数控制效果。

稍后会演示这个参数怎样运用。

去除符号信息后只能运用 dSYM 进行符号化,所以需求将 Debug Information Format 修改为 DWARF with dSYM file

ps:没有 DWARF 调试信息Xcode 靠什么来生成dSYM

仍是经过 DWARFXcode 的编译步骤:

  • 生成带有 DWARF 调试信息的可履行文件
  • 提取可履行文件中的信息打包成 dSYM
  • 运用 strip 指令去除符号化信息

iOS 静态库动态库看这里

Project -> BuildSettings -> Strip Style 设置为 Debugging Symbols(默许)

挑选不同的 Strip StyleAPP 构建终究一步的 Strip操作 会带上对应参数。

挑选 debugging symbols 在函数调用栈中,能够看到类名和办法名。

iOS 静态库动态库看这里

添加能够露出的头文件

iOS 静态库动态库看这里

3、生成XCConfig装备文件

iOS 静态库动态库看这里

先创立一个装备文件debugConfig.cxconfig,里边指定库以及主程序途径

iOS 静态库动态库看这里

指定运用装备文件

iOS 静态库动态库看这里

4、初步运转工程

编译成功后,能够看到产品里边会呈现动态库.framework及静态库.a文件

iOS 静态库动态库看这里
iOS 静态库动态库看这里

5、装备运转脚本

接下来,给xcode添加一个脚本,使得编译的时分能把符号表输出到终端。

在主程序的根目录下创立一个名为xcode_run_cmd.sh的脚本文件

iOS 静态库动态库看这里

代码直接贴出来便利大家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"

iOS 静态库动态库看这里

6、创立运转脚本的装备文件

翻开终端,输入tty,看到一个途径,记下来且不要封闭终端

iOS 静态库动态库看这里

创立一个名为runConfig.xcconfigXCConfig文件,而且添加如下代码

iOS 静态库动态库看这里

记住上面第一次创立的装备文件吗,由于XCode只支撑一份装备文件,所以不需求改动装备途径

直接在第一份装备里引证runConfig.xcconfig即可

#include "runConfig.xcconfig"

运转工程,接下来检查终端,现已输出的咱们的指令~

iOS 静态库动态库看这里

7、输出符号信息

runConfig.xcconfig里边添加检查符号表的指令

括号内称号便是debugConfig.xcconfig里装备的

// nm 检查符号表
// p:不分类 a:检查一切machO符号,了解成all
// ${地址}
CMD = nm -pa ${MYSTATICLIB_PATH}

iOS 静态库动态库看这里

在静态库内添加一个测验办法

iOS 静态库动态库看这里

再次运转代码能够在终端里看到,测验办法的符号

iOS 静态库动态库看这里

为了测验的便利和明晰,动态库和静态库都各添加上两个类

  • 一个揭露类PublibObj
  • 一个私有类PrivateObj 都添加上Test办法
    iOS 静态库动态库看这里

此刻输出动态库的符号表,能够看到不同类的办法是放在一起的

iOS 静态库动态库看这里

再输出静态库的符号表,看到符号表是依照.o文件分类输出的

这也能够印证静态库便是一个个.o文件的合集

iOS 静态库动态库看这里

终究输出主工程的符号表

iOS 静态库动态库看这里

8、运用动态库和静态库

主工程内创立一个文件夹并添加两个揭露类的引证,调用两个测验办法

iOS 静态库动态库看这里

再次检查终端,搜索静态库的办法,能查到其符号

iOS 静态库动态库看这里

再去查找动态库办法时,却发现找不到他的符号

iOS 静态库动态库看这里

其实上述现象正好印证了动态库和静态库的加载时期并不相同,脚本是在编译时运转的

  • 静态库的办法也是在编译期加载,所以这儿能获取到静态库的符号。

  • 动态库的办法则是在运转时加载,所以脚本运转时获取不到动态库的符号。

9、检查静态库的代码

经过objdump指令能够检查具体的代码情况

// objdump 检查
// -macho 检查macho格局的信息
// -d 打印代码块的内容
// ${地址}
CMD = objdump -macho -d ${APP_PATH}

iOS 静态库动态库看这里

能够看到动态库的代码只有不幸的两行,这儿其实是ViewDidLoad在调用

iOS 静态库动态库看这里

可是动态库的代码除了被调用以外,连实现都在这儿输出了

iOS 静态库动态库看这里

而且能楚的看到,静态库的代码和主程序的其他代码放在一块了~

这是由于静态库在编译时会仿制一份代码到大局符号表~

10、静态库的链接办法

前面现已叙述了下列几个参数只对链接静态库收效

  • -all_load:加载悉数代码
  • -noall_load:默许参数,屏蔽未用代码
  • -ObjC:加载悉数OC相关代码,包含分类
  • -force_load: 能够加载指定静态库的悉数代码

下面来稍稍验证一下~

ViewContrller的代码屏蔽

iOS 静态库动态库看这里

运转后在终端是无法找到任何与静态库相关的内容,由于主工程是默许敞开dead strip

iOS 静态库动态库看这里

Target -> Building Setting -> Other Linker Flag 设置为 -all_load 再次运转

iOS 静态库动态库看这里

从头搜索到staticPublicTest的实现,这就证明了 -all_load能够加载悉数代码

iOS 静态库动态库看这里

接下来把ViewContrller的代码屏蔽放开

iOS 静态库动态库看这里

Target -> Building Setting -> Other Linker Flag 设置为 -noall_load 再次运转

iOS 静态库动态库看这里

此刻报错无法运转。当然了,一切符号都不加载程序必定没法跑

iOS 静态库动态库看这里

-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

终端调用一下

iOS 静态库动态库看这里

创立成功~

iOS 静态库动态库看这里

怎样运用?

  • 1、跟一般的动态库相同~XCFramework仿制到工程里边
  • 2、Target -> General -> Frameworks,Libraries,and Embeddded Content 添加一下
    iOS 静态库动态库看这里
  • 3、然后import头文件调用就行

总结瞎掰

总结其实也没啥能总了…

便是最近沸点吐槽水文闹的沸反盈天…

这儿纯瞎说一下

这篇东西3天熬夜时断时续搞出来的,查资料、xcode调试、截图、码字要老命了…

质量嘛,自我感觉还行,纯手打真手把手教程,小白跟着也能走完整个过程

可是也有或许被以为是水文,毕竟没有很深奥的东西

其实吧

像我记性欠好的人,喜欢记载一下折腾过的东西和踩过的坑

以后用到的时分翻一下自己的主页就找到,也不必去搜,便利省时间

万一有相同问题的朋友能搜到看到,那帮到人就更开心~

技能社区嘛,技能的东西多容纳~