静态库and动态库

1,370 阅读7分钟

静态库简介

    1. 什么是库?
    • 库 就是程序代码的集合, 是共享程序代码的一种方式
    1. 库的分类?
    • 开源库
      • 公开源代码, 能看到具体实现
      • 例如MJExtension, MJRefresh, AFNetworking...
    • 闭源库
      • 不公开源代码, 是经过编译后的二进制文件, 看不到具体实现
      • 主要分为: 静态库 和 动态库
    1. 静态库的存在形式?
    • .a
    • .framework
    1. 动态库的存在形式?
    • .dylib
    • .tbd
    • .framework
    1. 静态库和动态库的区别?
    • 静态库在链接时, 会被完整的复制到可执行文件中; 被多次使用, 就有多份拷贝;
    • 动态库则不会复制, 只有一份. 程序运行时动态加载到内存; 系统只加载一次, 多个程序共用, 节省内存;
    • 但是!!!! 项目中如果使用到自己的动态库, 不允许上架!
    • 再但是!!! WWDC2014上公布的 苹果对ios8开放动态加载dylib的接口 也就是说 开放了动态库挂载
    1. 静态库应用场景?
    • 保护自己的核心代码
    • 国内的企业,掌握有核心技术,同时是又希望更多的程序员来使用其技术,因此采用"闭源"的方式开发使用
    • 例如:百度地图,友盟,JPush等
    • 将MRC的项目,打包成静态库, 可以在ARC下直接使用, 不需要转换
    • 提高工程的编译速度
    1. 静态库的特点?
    • .a + .h
    • 看不到具体实现的代码

静态库的制作(.a)

  • 生成静态库大步骤
      1. 创建项目时, 直接选择静态库(.a)
      1. 设置需要暴漏的头文件
      • TARGETS ->Build Phases ->Copy Files 把需要暴漏的头文件添加进来即可
      1. 在模拟器环境下编译(得到模拟器环境下的静态库)-选择模拟器6S,编译
      1. 在真机环境下编译(得到真机环境下的静态库)
  • 静态库使用测试
    • 1.使用模拟器的静态库, 拖入测试工程
      • 使用6S模拟器进行测试, 通过
      • 使用真机, 编译; 失败
      • 使用低型号模拟器测试,; 失败
    • 2.测试结果分析
      • 模拟器下的静态库和真机下的静态库不能共用(各个模拟器型号之间架构也不一样)
      • 主要原因是模拟器和真机CPU架构不一样
      1. 注意静态库所支持的架构
      • 不同机型的CPU, 对应的架构不同;
      • 模拟器:
        • 4s----5 : i386
        • 5s----6sPlus : x86_64
      • 真机:
        • 3gs---4s : armv7
        • 5/5c : armv7s(armv7兼容armv7s)
        • 5s---6sPlus: arm64
      • 查看静态库支持的架构
        • lipo -info 库文件
        • 分别选中不同的模拟器, 进行编译, 查看不同的静态库支持架构
      1. 怎样一次编译支持多个架构的的静态库?
      • 问题描述:
      • 正常情况下, 需要选中每一个模拟器进行编译, 生成支持对应架构的静态库. 然后合并; 非常蛋疼
      • 解决方案:
      • Build Settings -> Build Active -> NO
      • 表示不止编译活跃的架构, 让所有的架构都编译
  • 静态库文件的版本
    • 调试版本
      • 1.真机-Debug版本
      • 2.模拟器-Debug版本
      • 特点
        • 调试版本会包含完整的符号信息,以方便调试
        • 调试版本不会对代码进行优化
    • 发布版本
      • 1.真机-Release版本
      • 2.模拟器-Release版本
      • 特点
        • 发布版本不会包含完整的符号信息
        • 发布版本的执行代码是进行过优化的
        • 发布版本的大小会比调试版本的略小
        • 在执行速度方面,发布版本会更快些,但不意味着会有显著的提升
    • 怎样生成不同版本?
      • 项目 -> Edit Scheme -> Run -> Release/Debug 分别进行编译
    • 如果想要一个静态库, 既可以在模拟器上运行, 也可以在真机上运行怎么做?
    • 因为静态库针对于模拟器和真机生成了不同版本(支持不同架构), 所以没法同时运行
    • 解决方案: 静态库的合并
      • 检测.a的类型 $ lipo -info libCZTools.a
      • 合并.a lipo -create Debug-iphoneos/libTools.a Debug-iphonesimulator/libTools.a -output libTools.a
      • 特点
      • 合并.a的好处,开发过程中既可以在真机上调试,也可以在模拟器上调试
      • 合并.a的坏处,如果静态库太大,合并打包后,会非常大,因此很多第三方的静态库的.a是区分版本的
      • 今后在使用.a时一定注意版本
    • 补充: 拆解指定架构的库
      • lipo -thin 架构名称 架构路径 -output 目标路径

静态库制作(.framework)

    1. 新建工程, 直接选择.framework静态库
    1. 编译时, 设置编译所有架构 image.png
    1. 默认制作的是动态库, 需要设置链接类型
    • target -> Build Settings-> 搜索 Mach-o Type ; 改为静态库
      image.png

总结

  • 1.静态库打包的完整正确步骤!!
      1. 确定是静态库
      • .a 的肯定是静态库
      • .framework的需要设置链接类型
      • target -> Build Settings-> 搜索 Mach-o Type ; 改为静态库 image.png
      1. 确定支持模拟器或者真机中的所有架构
      • Build Settings -> Build Active -> NO
      • 表示不止编译活跃的架构, 让所有的架构都编译
      1. 提供的静态库应该是release版本
      • 项目 -> Edit Scheme -> Run -> Release/Debug 分别进行编译
    1. .a静态库和.framework静态库的区别?
    • .a是一个纯二进制文件, .framework中除了有二进制文件之外还有资源文件
    • .a文件不能直接使用, 至少要有.h文件的配合; .framework文件可以直接使用
    • .a + .h + sourceFile = .framework
    • 建议使用.framework
    1. 静态库开发中的常见问题
    • 问题1: 有些第三方库会使用到一些图片素材,例如公司的logo等。
    • 由于Xcode默认在编译时会把所有的素材文件导入到mainBundle中,可能与使用静态库的程序冲突。
    • 解决方案:
    • 在静态库中如果要使用图片素材,会利用bundle的手段
      • 1.建立bundle,并且向其中添加图片 source.bundle 的一个文件夹而已
      • 2.创建一个类方法,返回图片
      • 3.编译
      • 4.调用方如果需要使用,需要导入 .h + .a + XXX.bundle
    • 问题2:如果用户需要导入的头文件过多怎么加?
      • 建议使用一个主头文件包含其他头文件, 让用户只导入一个主头文件
    • 问题3: 静态库程序怎样测试?
    • 静态库本身就是一个小项目, 实现某些功能, 但是这些功能在开发中也需要测试. 而测试代码又不能作为静态库的一部分
    • 解决方案:
      • 创建复合项目

静态库操作

  • 合成不同架构的库
    • lipo -create xx xx -output xxx
  • 分解合成库
    • lipo -thin 架构名称 xx -output xx
  • 从合成库移除某个架构
    • lipo -remove 架构名称 xx -output xx
  • 查看是否是动态库
    • file xx

二进制化实践

  • 手动打包方案
    • 添加库target 根据情况选择.a/.framework
    • 导入文件引用
    • 生成库
    • 移动库到pod库内
    • 修改podspec
s.source_files = 'XMGSegmentBar/Products/include/**'
s.public_header_files = 'XMGSegmentBar/Products/include/*.h'
s.ios.vendored_libraries = 'XMGSegmentBar/Products/lib/libXMGSegmentBarLib.a'
    spec.ios.vendored_frameworks =
  • 自动打包方案
    • cocoapods-packager
      • 作用 快速完成类库的打包
      • 安装 sudo gem install cocoapods-packager
      • 使用
        • pod package spec文件名
        • pod package spec文件名称 --library --exclude-deps --spec-sources=spec文件私有索引库路径,github.com/CocoaPods/S…
      • 注意问题: 必须是已经上传代码到远程仓库

源码-二进制的切换

  • 解决问题 有时候宿主工程中, 需要看到具体源码, 想要在源码/二进制文件间来回切换
  • 原理 通过设置环境变量的取值, 控制不同的podspec流程走向
  • 示例代码
if ENV['IS_BINARY']
    s.source_files = 'XMGSegmentBar/Classes/*.h'
    s.public_header_files = 'XMGSegmentBar/Classes/*.h'
    s.ios.vendored_libraries = 'XMGSegmentBar/Products/lib/libXMGSegmentBarLib.a'

  else
    s.source_files = 'XMGSegmentBar/Classes/**/*'
  end

使用

  • 注意清除缓存
    • pod cache clean --all
  • 删除原有的项目pod文件
    • 安装之前, 注意添加设置的环境变量
      • IS_BINARY=1 pod install