编译器之打包动态/静态库

hivo3646 4年前
   <h2><strong>一、简介</strong></h2>    <h3><strong>I. 库的类型分类</strong></h3>    <p>静态库</p>    <ul>     <li>以 .a 或 .framework 为文件后缀名<br> .a 是一个二进制文件,不能直接拿来用,使用时需要 .a 文件 + 头文件 + 资源文件<br> 静态库打包时, <strong>只能打包代码</strong> ,图片文件、本地 JSON 文件和 xib 等资源文件无法打包进去</li>     <li>静态库 <strong>连接时</strong> 完整的复制到可执行文件中,多个文件使用,多份拷贝</li>     <li>OC 建议使用静态库,OC 使用动态库审核可能会不通过</li>     <li>静态库无法再包含其他的 .a 静态库。 <strong>只能把源码放进去一起编译</strong></li>    </ul>    <p>动态库</p>    <ul>     <li>以 .dylib 或 .framework 为文件后缀名「Xcode7 后 .dylib 变为 .tbd 文件」<br> .framework 文件,能直接拿来用。 .framework 文件 = 二进制文件(.a 文件 + .h 文件) + 资源文件<br> .tbd 文件,只是一个文本文件,其中包含架构信息,以及在真实运行时候二进制所在的位置,以及包含了动态库的符号表还有类的一些信息,这些信息在编译阶段足够了。「减少了所有设备 SDK 二进制动态库的体积」</li>     <li>动态库 <strong>运行时</strong> 动态加载到内存,只加载一次,多个文件公用,节省内存</li>     <li>Swift 只能使用动态库,不支持静态库</li>     <li>大部分第三方库就是动态库,可以暴露出来,放在源码的外部引用使用</li>     <li>动态库的特性使得 <strong>软件版本实时模块升级</strong> 、应用插件化、etc</li>    </ul>    <h3><strong>II. iOS 设备的 CPU 架构</strong></h3>    <p>iOS 库的打包,根据 CPU 架构的不同而不同</p>    <p>架构不同, <strong>不能编译通过</strong></p>    <p>模拟器的 CPU 架构</p>    <ul>     <li>i386:iPhone 4s ~ 5</li>     <li>x86_64:iPhone 5s ~ 7 Plus</li>    </ul>    <p>真机的 CPU 架构</p>    <ul>     <li>armv6:iPhone、iPhone 2、iPhone 3G、iPod Touch「第一代」、iPod Touch「第二代」</li>     <li>armv7:iPhone 3Gs、iPhone 4、iPhone 4s、iPad、iPad 2</li>     <li>armv7s:iPhone 5、iPhone 5c 「静态库只要支持了 armv7,就可以在 armv7s 的架构上运行」</li>     <li>armv64:iPhone 5s、iPhone 6、iPhone 6 Plus、iPhone 6s、iPhone 6s Plus、iPad Air、iPad Air2、iPad mini2、iPad mini3</li>    </ul>    <h2><strong>二、打包静态库</strong></h2>    <h3><strong>I. .a 文件 静态库打包</strong></h3>    <ol>     <li> <p>创建静态库的工程</p> <p>创建后执行 运行 或 编译 ,都可以生成静态库「保存在项目的 Products 中」</p> </li>    </ol>    <p><img src="https://simg.open-open.com/show/e566489501f3848664b2848bc414cd21.png"></p>    <ol>     <li> <p>通过在不同架构的设备下 编译/运行 生成支持不同架构的静态库</p> </li>    </ol>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/85cfa0d5b85681bca289910ff59afb03.png"></p>    <ol>     <li> <p>在 编译/运行 时, <strong>所有项目</strong> 都可以设置是 Debug 还是 Release</p> </li>    </ol>    <p><img src="https://simg.open-open.com/show/e62176a6bafe39ca3737a26852c98f85.png"></p>    <ol>     <li>设置库的接口 头文件</li>    </ol>    <p><img src="https://simg.open-open.com/show/85fbfa754b4043ad2670f47c7a7e9779.png"></p>    <ol>     <li> <p>通过设置不仅在当前运行的 CPU 架构上,适配所有的机型号架构</p> <p>一般 debug = no,release = yes 为了 debug 的时候编译更快「只编译连接当前的 CPU 架构所用的包」</p> <p>可以控制 测试版本 和 发布版本 是否都不仅仅能在真机上使用</p> </li>    </ol>    <p><img src="https://simg.open-open.com/show/7c55b00ff0fc9ab7edf30615df98ba94.png"></p>    <h3><strong>II. .framework 文件 静态库打包</strong></h3>    <ol>     <li> <p>前三步和 在 .a 文件 打包方式一致</p> </li>     <li> <p>设置库的接口 头文件</p> </li>    </ol>    <p><img src="https://simg.open-open.com/show/d10c1ce9bc27843d0406dc13bad7a21b.png"></p>    <ol>     <li> <p>设置打包的是 静态库,因为动态库也可以以 framework 的形式存在</p> <p>设为Static Library「这个默认选项是动态的」</p> </li>    </ol>    <p><img src="https://simg.open-open.com/show/5b78589be5b01a4cfbd55eaa30801008.png"></p>    <h3><strong>III. 使用静态库</strong></h3>    <p><strong>1. 防止项目中的文件和 静态库的同名文件在运行时会覆盖,只保留一张图片</strong></p>    <ul>     <li>把图片文件单独的放在一个 .bundle 文件中「一般 .bundle 的名字和 .a 或 .framework 的名字相同」</li>     <li>.bundle 文件制作方法:将文件夹,重命名为 XXX.bundle<br> 同理,其他资源文件也放在一个 .bundle 中</li>    </ul>    <p><strong>2. 在使用 category 静态库的工程中,调用方法时,会出现找不到该方法的运行时错误: selector not recognized</strong></p>    <p>原因:</p>    <ul>     <li>Unix 的标准静态库实现和 Objective-C 的动态特性之间有一些冲突</li>     <li>Objective-C 没有为每个函数定义链接符号,它 <strong>只为每个类创建链接符号</strong></li>     <li>当在一个静态库中使用类别来扩展已有类的时候,链接器不知道如何把类原有的方法和类别中的方法整合起来</li>    </ul>    <p>解决办法:</p>    <ul>     <li>在使用静态库的工程中配置 other linkerflags 的值为 -ObjC 如果还崩溃,在添加 -all_load 「或者 -force_load 」</li>     <li>all_load 作用于所有的库,而 -force_load 后面必须要指定具体的文件<br> 这样,会将静态库中所有和对象相关的文件都加载进来</li>    </ul>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/48c664b53e7c43542ac3f425a2d6f28d.png"></p>    <p><strong>3. 避免暴露过多的 .h 文件</strong></p>    <p>使用</p>    <ul>     <li>在静态库的内部创建一个 .h 文件「一般这个.h文件的名字和静态库的名字相同」<br> 然后把所有需要暴露出来的 .h 文件名都集中放在这个 .h 文件中,只需要把这个 .h 暴露出来</li>     <li>一般这个总头文件内部引入其他头文件的格式为<br> #import <framework名字/其他头文件.h> ,这是以调用这个库的 <strong>项目的角度来写的</strong></li>    </ul>    <p><strong>搜索头文件</strong></p>    <ul>     <li> <p>Header Search Paths</p> <p>系统的搜索路径,用来引入项目中 <strong>没有添加</strong> 的 header 文件</p> <p>通过 #import <名称.h> 来引入</p> </li>     <li> <p>User Header Search Paths</p> <p>用户的搜索路径,用来引入项目中的 <strong>已添加</strong> 的 header 文件</p> <p>通过 #import <名称.h> 或 #import "名称.h" 来引入</p> </li>     <li> <p>Always Search User Paths</p> <p>Header Search Paths 作为系统级别路径一定会被搜索</p> <p>设 Always Search User Paths 为 YES,编译器会先搜索 User Header Search Paths 路径下的目录</p> <p>设 Always Search User Paths 为 NO,编译器不会搜索 User Header Search Paths 路径下的目录「默认」</p> </li>     <li> <p>设置格式举例: $(SRCROOT)/项目名称/AppDelegate.h<br> $(SRCROOT) 宏和 $(PROJECT_DIR) 宏都指 xxx.xcodeproj 所在的 <strong>父目录</strong> ,新建项目后的目录为:</p> </li>    </ul>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/0fba5022a0b8253b19858f6cb14ae2ab.png"></p>    <ul>     <li> <p>Project的 Building Settings 中得设置 <strong>默认</strong> 并不被 <strong>Targets</strong> 继承</p> <p>只有 Targets 的设置加入了 $(inherited) 时才被继承,添加目录的时候写上 $(inherited) 就表示从 frameworks 里面读取</p> </li>     <li> <p>设置查找路径的参数</p> <p>recursive:遍历该目录,编译的时候在找库的路径的时候,会遍历该目录下的所有子目录的库文件</p> <p>non-recursive:不遍历该目录「默认,推荐使用,可减少编译速度」</p> </li>    </ul>    <p><strong>4. 拖拽项目/将项目当做静态库处理</strong></p>    <ul>     <li>给当前项目在 Link Binary With Libraries 中添加拖拽项目的 <strong>库文件</strong></li>     <li>如果当前的 Target 需要依赖其他库文件,在 Target Dependencies 中添加所需的库文件</li>     <li>如果库文件在 Link Binary With Libraries 中已经添加 <strong>「必要」</strong> ,但也可能需要在 Target Dependencies 中再次添加 <strong>「非必要」</strong></li>    </ul>    <p><img src="https://simg.open-open.com/show/e7f83d40a8b2bc2b7a6ae4f09b6c2c53.png"></p>    <p><strong>5. 其他</strong></p>    <ul>     <li> <p>查看静态库所支持的 CPU 架构</p> <p>在命令行找到静态库所在的文件夹,执行 lipo -info 静态库文件名 命令</p> </li>     <li> <p>合并静态库</p> <p>在命令行找到静态库所在的文件夹,执行 lipo -create 静态库1 静态库2 -output 新静态库名称.a 命令</p> </li>    </ul>    <h2><strong>三、打包动态库</strong></h2>    <p><strong>1. 制作、编译过程与静态库相同</strong></p>    <p><strong>2. 在 Embedded Binaries 中添加动态库</strong></p>    <p><img src="https://simg.open-open.com/show/96cbeda3aac7f3045faf05084aa44c1a.png"></p>    <p>Paste_Image.png</p>    <p><strong>3. 合并动态库文件</strong></p>    <p>合并动态库文件并非合并的是 .framework 文件,而是其中的二进制代码文件</p>    <p><strong>4. 动态库的使用注意</strong></p>    <h3><strong>I. 自定义的 .a 静态库,不可以包含动态库</strong></h3>    <h3><strong>II. 自定义的 .framework 静态库,可以包含动态库文件</strong></h3>    <p>绕过 Xcode 的 UI 界面来连接动态库,步骤如下</p>    <ul>     <li> <p>项目中不需要引用连接的动态库「.tbd 的动态库」</p> </li>     <li> <p>通过格式: -l<library_name> 添加动态库,例:添加 libiconv.tdb 记为 -liconv</p> </li>    </ul>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/77fbf72dbec4c49fb96b3c868af90580.png"></p>    <p>缺陷</p>    <ul>     <li> <p>虽然自定义的静态库中已经导入了动态库 X.tbd ,但是,当自定义的 framework 的静态库被调用时,可能需要再次导入动态库 X.tbd 到当前项目中</p> </li>    </ul>    <p> </p>    <p>来自:http://www.jianshu.com/p/543965d30c16</p>    <p> </p>