iOS静态库和动态库

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u014084081/article/details/78737918

iOS静态库和动态库

静态库和动态库是什么,以及它们的区别,详细介绍可参考博文:iOS里的动态库和静态库,里面讲的很详细。

静态库动态库的区别

内容来源自:iOS动态库与静态库

静态库和动态库是相对编译期和运行期的:静态库在程序编译时会被链接到目标代码中,程序运行时将不再需要改静态库;而动态库在程序编译时并不会被链接到目标代码中,只是在程序运行时才被载入,因为在程序运行期间还需要动态库的存在。
静态库 好处

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

动态库 好处:

  1. 使用动态库,可以将最终可执行文件体积缩小,将整个应用程序分模块,团队合作,进行分工,影响比较小
  2. 使用动态库,多个应用程序共享内存中得同一份库文件,节省资源
  3. 使用动态库,可以不重新编译连接可执行程序的前提下,更新动态库文件达到更新应用程序的目的。
  4. 应用插件化
  5. 软件版本实时模块升级
  6. 在其它大部分平台上,动态库都可以用于不同应用间共享, 共享可执行文件,这就大大节省了内存。
    iOS平台 在 iOS8 之前,苹果不允许第三方框架使用动态方式加载,从 iOS8 开始允许开发者有条件地创建和使用动态框架,这种框架叫做 Cocoa Touch Framework。虽然同样是动态框架,但是和系统 framework 不同,app 中使用 Cocoa Touch Framework 制作的动态库 在打包和提交 app 时会被放到 app main bundle 的根目录 中,运行在沙盒里,而不是系统中。也就是说,不同的 app 就算使用了同样的 framework,但还是会有多份的框架被分别签名,打包和加载。不过 iOS8 上开放了 App Extension 功能,可以为一个应用创建插件,这样主app和插件之间共享动态库还是可行的。
    苹果系统专属的framework 是共享的(如UIKit), 但是我们自己使用 Cocoa Touch Framework 制作的动态库是放到 app bundle 中,运行在沙盒中的

静态库和动态库的存在的形式

  1. 静态库:以.a 和 .framework为文件后缀名。
  2. 动态库:以.tbd(之前叫.dylib) 和 .framework 为文件后缀名。(系统直接提供给我们的framework都是动态库!)

理解:.a是一个纯二进制文件,.framework中除了有二进制文件之外还有资源文件。 .a ,要有 .h 文件以及资源文件配合, .framework 文件可以直接使用。总的来说,.a + .h + sourceFile = .framework。所以创建静态库最好还是用.framework的形式

静态库和动态库的区别 
不同点:

  1. 静态库在链接时,会被完整的复制到可执行文件中,如果多个App都使用了同一个静态库,那么每个App都会拷贝一份,缺点是浪费内存。类似于定义一个基本变量,使用该基本变量是是新复制了一份数据,而不是原来定义的;
  2. 动态库不会复制,只有一份,程序运行时动态加载到内存中,系统只会加载一次,多个程序共用一份,节约了内存。类似于使用变量的内存地址一样,使用的是同一个变量;

共同点:

  1. 静态库和动态库都是闭源库,只能拿来满足某个功能的使用,不会暴露内部具体的代码信息

在文档Overview of Dynamic Libraries有如下的2张图:

1.App using static libraries

1
2.App using dynamic libraries

App using dynamic libraries

在这里记录下学习的过程

静态库

参考iOS静态库SDK制作(包含第三方静态库)

平时我们用的第三方SDK基本上都是静态库,静态库的几个特点:

  • 在App项目编译的时候会被拷贝一份编译到目标程序中,相当于将静态库嵌入了,所以得到的App二进制文件会变大。
  • 在使用的时候,需要手动导入静态库所依赖的其他类库。(比如说某个SDK中使用到了CoreMotion.framework,在使用的时候需要手动导入。有的SDK需要link十几个系统库,这个时候非常恶心,只能一个一个手动加,这是静态库一个很大的不便之处。)
  • 导入静态库的应用可以减少对外界的依赖,如果导入的是第三方动态库,动态库找不到的话应用就会崩掉,例如Linux上经常出现的lib not found
  • 静态库很大的一个优点是减少耦合性,因为静态库中是不可以包含其他静态库的,使用的时候要另外导入它的依赖库,最大限度的保证了每一个静态库都是独立的,不会重复引用。

创建静态库

选择如下的Cocoa Touch Static Library创建静态库

1

Build生成静态库,在Products文件夹中查看
2

如果在Edit Scheme中,Build Configuration中选择Debug(以下是我自己的试验)

  • 如果模拟器选择的是Generic iOS Device,会有Debug-iphoneos文件夹
    3

  • 如果模拟器选择的是其它的模拟器,会有Debug-iphonesimulator文件夹
    4

Debug-iphonesimulator文件夹下对应的libWZLib.a是对应模拟器的,可使用lipo -info libWZLib.a来验证,如:
验证1

输出信息如下,其架构为x86_64

input file libWZLib.a is not a fat file
Non-fat file: libWZLib.a is architecture: x86_64

同理可验证Debug-iphoneos文件夹下的libWZLib.a,其架构为armv7 arm64,表示对应真机


如果在Edit Scheme中,Build Configuration中选择Release,可生成其它的2个版本的静态库。Release表示真实环境,会把日志和断言都去掉。


上面生成的Debug-iphoneosDebug-iphonesimulator文件夹中,头文件WZLib.h是位于include/项目名下,如何让头文件,直接在include文件夹下呢?
Build Phases中的Copy Files中,去掉Subpathinclude/$(PRODUCT_NAME),后面的/$(PRODUCT_NAME)(表示工程文件名)即可:
5
删除原来生成的文件,Clean之后,再Build,此时头文件就直接在include文件夹下了
6

如果想添加多个头文件,选择Copy Files下的+号,如下:

7

静态库使用

如果把对应真机的静态库libWZLib.a和头文件,导入到项目中使用,则会提示错误:

8

所以要导入模拟器对应的版本

如果想同时支持模拟器和真机,该怎么做呢?
需要使用终端命令来合并
如下合并命令,表示把libWZLibR.a(真机版)和libWZLib.a(模拟器版),合并为test.a

lipo -create libWZLibR.a libWZLib.a -output test.a

此时lipo -info test.a,显示信息为:

Architectures in the fat file: test.a are: armv7 x86_64 arm64 

表示其支持模拟器和真机

要注意的问题

1.如果静态库内有 category 分类,那么需要在添加 -ObjC 编译标识,否则可能会报:unrecognized selector sent to instance

可参考:

使用Bundle打包图片配合静态库使用

可参考:

由于静态库只能放代码不能带图片资源,有两种解决方案

  • 使用Framework<( ̄︶ ̄)>
  • 使用bundle打包图片文件和静态库分别打包拷贝给别人
  • 把图片素材传给别人

第三种方法这么坑,万一哪个文件再搞混了….所以推荐用第二种

新建工程:File -> New -> Project… -> OS X -> Framework & Library -> Bundle
我们必须借助于OS X,因为iOS框架中没有创建Bundle的模板

动态库

参考iOS静态库SDK制作(包含第三方静态库)

这个是我们最常用的一类库,使用频率最高的UIKit.framework和Fundation.framework都属于动态库,所有.dylib和.tbd结尾的都属于动态库。动态库的几个特点:

  • 平时使用的系统库都放在iOS系统中,在你打包应用程序的时候这些库不会拷贝到你的程序中,当需要使用的时候会动态从iOS系统中加载它们,因为这个原因,动态库也被称作共享库。编译时才载入的特性,也可以让我们随时对库进行替换,而不需要重新编译代码。
  • 这些库是所有应用公用的,换一种说法就是节省了应用安装包的体积,这是区别静态库很重要的一个特点,因为静态库使用一次就要拷贝一次,非常浪费资源。
  • 动态库在制作的时候可以直接包含静态库,也能自动link所需要的依赖库。
  • 使用动态库的时候不需要再次link依赖库,即导即用,这个就厉害了。唯一需要注意的是在导入自己制作的动态库时,需要在Embedded Binaries中导入,不然会报错:image not found。此时这个动态库会跟静态库一样被拷贝到目标程序中进行编译,苹果又把这种Framework叫做Embedded Framework

关于动态库要搞清楚一点,我们自己制作的动态库与系统动态库的区别,我们自己制作的动态库引入App项目的时候需要embed进项目,也就是要拷贝到目标程序中,这就有点不像动态库的特性了,苹果这么做也是考虑安全问题吧!

创建动态库

9

添加相应的类后,在Build Phases下的Headers中,把Project下的头文件移动到Public即可,如下:
10

此时build,在工程的的Products目录下,生成WZFrame.framework。与静态库一样,也对应真机和模拟器版本

11

合并真机和模拟器的Framework

1.使用lipo -create,这种方式比较麻烦一点,生成后还需要替换,具体方式可参考:

2.在工程的Build Phases里添加以下脚本,真机和模拟器都Build一遍之后就会在工程目录下生成Products文件夹,里面就是合并之后的Framework

  • 新建New Run Script Phase
    15

  • 添加脚本
    16

    if [ "${ACTION}" = "build" ]
    then
    INSTALL_DIR=${SRCROOT}/Products/${PROJECT_NAME}.framework
    
    DEVICE_DIR=${BUILD_ROOT}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework
    
    SIMULATOR_DIR=${BUILD_ROOT}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework
    
    
    if [ -d "${INSTALL_DIR}" ]
    then
    rm -rf "${INSTALL_DIR}"
    fi
    
    mkdir -p "${INSTALL_DIR}"
    
    cp -R "${DEVICE_DIR}/" "${INSTALL_DIR}/"
    #ditto "${DEVICE_DIR}/Headers" "${INSTALL_DIR}/Headers"
    
    lipo -create "${DEVICE_DIR}/${PROJECT_NAME}" "${SIMULATOR_DIR}/${PROJECT_NAME}" -output "${INSTALL_DIR}/${PROJECT_NAME}"
    
    #open "${DEVICE_DIR}"
    #open "${SRCROOT}/Products"
    fi
    
  • 上面的脚本要保证真机和模拟器的framework都要存在,否则生成的framework会为空。此时生成的framework,在项目目录下
    16

此时使用lipo -info WZFrame,结果为:

Architectures in the fat file: WZFrame are: x86_64 armv7 arm64 

表示已合并成功

动态库使用

如果把刚才生成的动态库WZFrame.framework,导入并调用

#import <WZFrame/WZObject.h>

[WZObject testMethod];

添加项目中来直接使用,会出现如下所示的错误

12

dyld: Library not loaded: @rpath/WZFrame.framework/WZFrame
  Referenced from: /Users/Miller/Library/Developer/CoreSimulator/Devices/15E3C2C6-797C-40CF-A0F5-8DCA6E472AF1/data/Containers/Bundle/Application/2F13129C-2F69-48BC-B100-00A2D478C4B3/iOSLibTest.app/iOSLibTest
  Reason: image not found

如何解决呢?
1.选择New Copy Files Phase
13

2.添加WZFrame.framework
14

展开阅读全文

没有更多推荐了,返回首页