70

iOS的静态库和动态库

 5 years ago
source link: https://www.titanjun.top/2018/09/12/iOS的静态库和动态库/?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

VBnqqiY.png!web

静态库简介

什么是库?

  • 库从本质上来说是一种可执行代码的二进制格式,可以被载入内存中执行
  • 库就是程序代码的集合, 是共享程序代码的一种方式
  • 库从广义上可分为开源库和闭源库, 而闭源库才分为静态库和动态库
    • 开源库: 对外公开源代码, 能看到具体的代码实现, 例如 Github 上面的第三方开源库都称之为开源库
    • 闭源库: 不公开源代码, 文件是经过编译后的二进制文件, 看不到具体实现, 例如 .a 文件和 .framework 文件

静态库与动态库

  • 静态库:链接时完整地拷贝至可执行文件中,被多次使用就有多份冗余拷贝
    • 静态库有两种存在形式: .a.framework
  • 动态库:动态库则不会复制, 只有一份. 程序运行时动态加载到内存; 系统只加载一次, 多个程序共用, 节省内存
    • 动态库有两种存在形式: .dylib.framework
    • 需要注意的是: 系统的 .framework 是动态库,我们自己建立的 .framework 是静态库
  • 但是项目中如果使用到自己的动态库, 苹果是不允许上架!
  • 再但是 WWDC2014 上公布的苹果对 ios8 开放动态加载 dylib 的接口 也就是说 开放了动态库挂载, 但是目前几乎没有上架的项目使用

.a.framework 的区别

  • .a 是一个纯二进制文件不能直接使用, 必须要有 .h 文件才能使用, .h 文件对外提供接口, .a 文件是代码的具体实现, 即 .m
  • .framework 中除了有二进制文件之外还有资源文件, 可以直接使用
  • 所以开发中建议使用 .framework

为什么要使用静态库?

  • 保护自己的核心代码, 国内的企业,掌握有核心技术,同时是又希望更多的程序员来使用其技术,因此采用”闭源”的方式开发使用
  • 实现iOS项目的组件化, 可以把固定的业务模块编译成静态库
  • 开发第三方SDK, 例如: 友盟SDK, 百度地图SDK….
  • 提高项目的编译速度, 比如项目的组件化, 虽然使用了组件化, 但依然是源码, 如果工程庞大, 编译速度依然非常慢, 但是如果把响应的功能和业务组件编译成静态库, 将会大大提高项目的编译速度

.a 静态库的生成和使用

nuUzIjM.png!web

  • 生成 .a 静态库, 这里我们选择第二个创建
  • 静态库分真机和模拟机两种环境
    • 在真机环境下编译出来的是只适用于真机的静态库
    • 在模拟机环境下编译出来的是只使用模拟机的静态库
    • 同事使用真机和模拟机的静态库, 后面会提到, 暂不赘述
  • 在不同的环境下静态库支持的架构也是不同的, 模拟器下的静态库和真机下的静态库不能共用, 不同型号编译的静态库也是不能共用的
  • 不同设备使用的CPU不同,从而使用的CPU架构(指令集)也不同,静态库有其支持的CPU架构,若静态库在不支持的CPU架构上运行程序就会崩溃

静态库的架构

// 1. 模拟器使用的CPU架构: 
iphone4s - iphone5 : i386 
iPhone5s - iPhoneX :x86_64

// 2. 真机使用的CPU架构: 
iPhone3gs - iPhone4s:armv7 
iPhone5 - iPhone5c:armv7s 
iPhone5s - iPhoneX:armv64
lipo -info 静态库名称
// 执行lipo -info的输出结果
$ lipo -info libStateLib.a 
input file libStateLib.a is not a fat file
Non-fat file: libStateLib.a is architecture: x86_64

编译多架构静态库

  • 在调试不同机型的过程中, 需要选中每一个模拟器进行编译, 生成支持对应架构的静态库然后合并, 非常蛋疼
  • 怎样一次编译支持多个架构的的静态库?

Qziy2mI.png!web

  • 该选项默认是 YES , 也就是只编译当前活跃环境的架构, 设置成 No 即可
  • 模拟器环境编译出来的就支持所有的模拟机型号, 真机亦是如此

静态库的版本

  • iOS 证书一样分调试版本( Debug )和发布版本 Release
  • 真机- Debug 版本
  • 真机- Release 版本
  • 模拟器- Debug 版本
  • 模拟器- Release 版本

调试版本 Debug

  • 真机- Debug 版本和模拟器- Debug 版本
  • 调试版本的特点
    • 调试版本会包含完整的符号信息,以方便调试
    • 调试版本不会对代码进行优化

发布版本 Release

  • 真机- Release 版本和模拟器- Release 版本
  • 发布版本的特点
    • 发布版本不会包含完整的符号信息,
    • 发布版本的执行代码是进行过优化的,
    • 发布版本的大小会比调试版本的略小,
    • 在执行速度方面,发布版本会更快些,但不意味着会有显著的提升

生成不同版本的静态库

选择项目 -> Edit Scheme -> Run -> Release/Debug 分别进行编译, 即可得到不同版本的静态库

qMFVbeB.png!web

生成 .a.h 文件

  • 正常情况下, 生成的 .h 文件是在 ../include/$(PRODUCT_NAME) 目录下的, $(PRODUCT_NAME) 指的是项目的名字
  • 修改图中2处的路径地址, 即可修改 .h 文件生成的路径, 填空即为和 .a 文件在同级目录下

mm2AnmY.png!web

最后编写好代码, command+B 编译之后, 如图所示操作即可找到生成的静态库

a2QnyyA.png!web

合并静态库

.a
// 以下所有方式得到的静态库都可以通过lipo -info xx.a方式检测现有的架构, 注意要在xx.a所在的目录下

// 1. 合并静态库
lipo -create 静态库1路径 静态库2路径 -output 合并后的静态库名称

// 示例
lipo -create /Users/xxx/Debug-iphoneos/libStateLib.a /Users/xxx/Debug-iphonesimulator/libStateLib.a -output hahah.a


// 2. 移除某一个架构
lipo -remove 架构名称 静态库绝对路径 -output 新的静态库名字
lipo -remove arm64 /Users/xxx/Build/Products/hahah.a -output ha_arm64.a


// 2. 拆分出一个单独架构的静态库
lipo -thin 架构名称 静态库绝对路径 -output 新的静态库名字
lipo -thin arm64 /Users/xxx/Build/Products/hahah.a -output only_arm64.a

.framework 静态库

生成 .framework 静态库

选择 framework

创建新工程, 选择第一个创建 Framework 工程

nuUzIjM.png!web

选择编译环境

选择适配所有真机或者适配所有模拟器(编译所有架构), Build Settings -> Build Active Architecture Only 选项设为 NO

手动设置静态库

刚创建的工程默认创建的是动态库, 需要手动设置链接类型, Build Settings -> Mach-o Type 设置成 Static Library

AnERze6.png!web

静态库版本

设置静态库的版本, 选择项目 -> Edit Scheme -> Run -> Release/Debug 分别进行编译, 即可得到不同版本的静态库

添加公开头文件

Target->Build Phases->Headers 中的 Project 中要暴露的头文件拖拽到 Pulic 里面:

mMjUFrN.gif

编译

分别选择 Generic iOS Device 和任意一个模拟器各编译一次。编译完,工程中 Products 文件夹下的 xxx.framework 由红色变成了黑色,然后 show in finder ,看看生成的文件

注意事项

  • 如果静态库中有 category 类,则在使用静态库的项目配置中 Other Linker Flags 需要添加参数 -ObjC 或者 -all_load
  • 如果创建的 framework 类中使用了 .tbd ,则需要在实际项目中导入 .tbd 动态库。
  • .framework 静态库的合并和拆分和 .a 静态库的方式一样, 就不在赘述了

bundle 加载资源

  • 由于 Xcode 默认在编译时会把所有的素材文件导入到 mainBundle 中,可能会让宿主工程与使用静态库的程序冲突。
  • 在创建静态库的项目中又是难免会用到一些图片或者 xib 等资源, 类似这些资源在静态库中又如何进行管理呢
  • 这里我们就要引入一个 bundle 文件, 对资源进行管理, 用以存放 xib 文件或者图片等资源
  • bundle 文件是是静态的,不进行编译的资源文件, 所以使用时需要找到相应的资源路径

创建方式

  • 把包含资源文件的文件夹的后缀改为 .bundle , 这时他就变成一个 bundle 文件
  • 或者右键显示包内容就可以把对应的图片资源等放进文件中,然后把他丢进工程中就可以使用了
  • 调用该图片时, 需要在图片名前加上 xxx.bundle 前缀

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK