美文网首页iOS DevelopmentiOS @IT·互联网
cocoapods的静态库和动态库

cocoapods的静态库和动态库

作者: sea_biscute | 来源:发表于2018-02-28 21:04 被阅读286次

    简介

    cocoapods在1.4.0推出了static framework,先扒扒历史原因.

    dymanic framework原因

    在iOS8以前,苹果只允许发布静态库,当然cocoapods只支持静态库,但是在iOS8苹果推出了APP extension的概念,可以对项目进行扩展,感兴趣的可以看APP extension.
    因为APP extension和主项目是两个独立的进程,为了共享代码,苹果允许我们创建动态库,即dynamic framework.

    swift第三方库

    在swift语言日益优化的前提下,我们想要进行项目swift化,但是在Xcode 6.0 Beta 4的 Release Notes 中,可以找到这句话:
    Xcode does not support building ``static libraries that include Swift code. (17181019)

    动态库导致的static library报错

    看了上面的原因,你会问pod直接使用动态库不就好了,但是对于pod来说,有这么几个问题

    • 包含静态库报错
      The 'xxx' target has transitive dependencies that include static binaries

    • 动态库不能依赖静态库

    ok,介绍完历史原因,我们继续看,在讲解适配前,先了解几个概念.

    基础介绍

    在OC环境下,绝大多数项目都使用cocoapods进行第三方库的管理,pod可以实现依赖管理,版本控制等功能,对于主项目X依赖A,A内部A->B,A->C,B→D,这类的依赖情况,主项目只需要引入A,在安装时就会检测其他的依赖pod是否存在,不存在进行安装.

    pod的管理,使得项目中同一类的库只存在一份,cocoapods的项目可以静态库 动态库二选其一,关于这两种的区别下面会做详细解释

    默认使用静态库管理,如果想改为动态,需要在podfile内部添加use_frameworks!字段,该字段告诉pod,使用框架的方式,安装和管理第三方库

    静态库不能包含swift文件,pod将第三方编译为static library,不能支持swift语言,新版的改为了framework的形式,下面介绍library和framework的区别.

    library和framework

    library仅能包含编译后的代码,即.a文件,不能包含其他的资源文件.

    但是我们封装的第三方库,有时需要包含.h文件,.nib文件,图片,文档扥g

    framework可以包含以上所有类型.且支持包含swift代码.

    framework支持iOS8以后,而static library可以追溯到iOS6.

    由于 iOS 的沙盒机制,自己创建的 Framework 和系统Framework 不同,App 中使用的 Framework 运行在沙盒里,而不是系统中.每个 App 都只能用自己对应签名的动态库,做不到多个 App 使用一个动态库

    区别总结

    动态库和静态库的区别如下

    动态库 静态库
    命名空间 有单独的命名空间,不同库同名文件不会冲突
    使用import<XXX/xxx.h>的方式引入
    没有单独命名空间,同名文件冲突
    引入方式import"xxx.h"
    加载时机 在启动时加载,加载时间较长 构建时加载
    依赖关系 可以依赖动态库,不能依赖静态库 可以依赖动态库和静态库
    是否能使用swift 可以包含swift文件 在cocoapods1.4.0之后,可以使用use_framework!的方式包含swift文件
    framework支持static_framework

    原理分析

    上面的总结我们知道静态库在程序启动时被加载,动态库在使用时被加载

    那么这些区别原理何在呢,下面分析下几个概念:

    编译,目标文件,符号表,链接

    编译: 编译器生成机器代码,生成目标文件.

    目标文件包含两种符号表: 1.文件转换后的符号(名称和方法的地址及偏移量) 2.未确定的符号(需要在链接阶段才能解析完成的机器代码)

    目标文件包含名为"main"的符号,可以将代码块加载进RAM运行.并将"main"作为符号表的运行入口的初始位置

    链接: 将我们的各种目标文件加上一些第三方库,和系统库链接为可执行文件

    链接主要决议符号,也就是变量函数等的地址

    • 若符号来⾃静态库(本质就是.o 的集合包)或 .o,将其纳⼊链接产物,并确定符号地址

    • 若符号来⾃动态库,打个标记,等启动的时候再说---交给 dyld 去加载和链接符号

    于是链接加装载就有了不同的情况

    Load 装载:将库⽂件载⼊内存

    • Static Loading:启动时

    • Dynamic Loading:启动后(使⽤时)
      Link 链接:决议符号地址

    • Static Linking:构建(链接)时

    • Dynamic Linking:运⾏时(启动时或使⽤时)

    静态,共享和动态库

    静态库只是目标文件的集合.静态库只是为了方便处理大量文件.链接器只选取需要的文件并将它们写入最终代码块,这使得静态链接程序很大.( This makes statically linked programs pretty large.)

    共享和动态库只需要被系统加载一次.然后使用该库的工程只需要对其进行引用即可.共享和动态库有两种创建方式

    1.全量的链接对象文件,包含大量的可被调用的符号表(真实的库代码)

    2.通过"stub"对象文件,包含可调用方法的映射表(jump table)

    通过动态库链接时,stub对象文件是通过类似静态库的形式被加载到程序中的.但是方法只是加载了方法声明.

    当程序使用动态库加载时,系统需要额外链接存储在RAM中的共享库.在加载系统共享库的stub文件时有个实现技巧.有两种方式可以实现加载系统共享库,1.系统拦截调用,进入系统,修改项目地址的上下文,转换到共享库,工作量很大.另一种方式,将静态库映射到运行程序通过虚拟内存管理的地址空间,这使得共享库对于多个项目来说,只是项目的一部分,虽然只在内存中短暂存在.

    这样的话,代码被共享,但是每个程序的堆栈由自己管理,使得各个程序员直接完全独立.

    结果

    静态库:稳定,但是占用内存空间.

    动态库:从系统加载代码,共享代码节约空间,但是可以会导致运行时的错误,且不易定位和修复.

    动态库详解

    苹果官方关于动态库的描述:

    动态库相比静态库,减少了app可执行文件的大小.并且可以只在使用时,按需加载而不是在启动时加载.这个特性减低了启动时间,并且更优秀的利用了内存.

    动态库不能依赖静态库

    启动时间过长解决办法

    1. 第三方框架swift-staticlibs,集成的为动态库,在构建阶段,转为静态库加载的形式,这样做的原因:

      1. Xcode的static library不能包含swift

      2. 动态库启动时间过长

    2. 使用static framework的方式,下面会做介绍

    静态库详解

    我们可以在Build Setting里面通过Mach-O Type查看target的动态或者静态状态

    [图片上传失败...(image-28b016-1519821296948)]

    cocoapods1.4.0对于static framework的支持

    static framework

    pod在1.4.0之后提供了静态框架的特性.过去的ues_framework!只能发布动态库,现在可以发布静态的框架.这一特性解决了过去动态框架不能依赖静态库的弊端.现在的静态framework也可以依赖静态库,也可以依赖通过vendored_frameworks发布的第三方框架.

    补充,vendored_frameworks和vendored_library是在podspec文件内使用的属性,用法是声明包含的第三方framework和library.

    背景

      1. static framework和library有什么区别呢? framework是对于library,头文件和资源等内容的封装.library可以是动态或者静态的,静态库在构建时期链接,但是动态库是在运行时才进行加载.
      2. 动态库不能依赖静态库是因为静态库不需要在运行时再次加载,如果多个动态库依赖同一个静态库,会出现多个静态库的拷贝,而这些拷贝本身只是对于内存空间的消耗.
      3. 另一个历史原因是,过去很多库是通过包含静态库的vendored_framework形式发布的.
      4. 在1.4.0之前,资源只能通过动态库的方式构建,所以不能依赖vendored_framework的库.而且对于vendored_framework的二进制库,无法在转换成资源pod时仍保持动态性

    以上原因,使得pod在1.4.0提供了静态框架的支持.用法简单,只需要在podspec文件内,声明如下即可

    s.static_framework = true

    限制

    所有swift库需要保持一致的版本,包含Swift文件的framework,必须指定swift的版本号,pod之后提供了新特性pod制定swift版本范围

    
    `Pod::Spec.``new` `do` `|s|`
    
    `s.name = ``'BanannaLib'`
    
    `s.version = ``'1.0.0'`
    
    `s.swift_version = ``'>= 3.2'`
    
    `s.source_files = ``'**/*.swift'`
    
    `end`
    
    

    支持包含swift文件

    在创建私有pod时,如果项目中包含swift文件,需要在podfile内部添加use_framework!字段,如果不添加会报以下错误

    
    [!] Pods written in Swift can only be integrated as frameworks; add `use_frameworks!` to your Podfile or target to opt into using it. The Swift Pod being used is: erp-boss-common-ios
    

    如果框架已经声明了static_framework = true,则可以包含swift文件,且可以依赖其他的静态库.

    苹果官方的Xcode9发布文档有以下说明Xcode release文档

    
    Xcode supports static library targets which contain Swift code. Debugging applications that use Swift static libraries may require a complete set of build artifacts that are in their original location. (``33297067``)`
    

    Xcode支持包含swift代码的静态库项目.

    tips:

    包含.a的static library,可以用lipo查看.a库所支持的架构.

    
    lipo -info libTestLib.a 
    
    Architectures in the fat file: libTestLib.a are: armv7 i386 x86_64 arm64
    
    

    静态库报错

    错误

    在我们使用use_frameworks!的时候,会遇到类似于下面的错误提示,引起这种提示的原因,和各种情况,下面分析一下.

    
    [!] The 'Pods-testDynamic_Example'` `target has transitive dependencies that include static binaries: (/Users/zhaoyanan/Documents/projects/pod/testDynamic/Example/Pods/SAKC-Ares/lib/libcares_iOS.a)
    
    

    boss->A->B->SL(static library)

    boss为主项目

    SL为包含.a的静态库

    boss->SL,没有问题

    在B内增加static_framework = true, 可以解决boss->B->C问题

    对于boss->A->B->C的情况,如果A,A',A''都依赖了B,需要保证他们依赖的方式相同,即都不指定版本号,都在都指定特定的版本号,或者都指定相同的范围,声明不同,则会报错.

    s.static_framework = true, s.subspec不需要再设置

    其他方法

    有一种做法,可以作为参考,没有测试,对于A->B->C-SL的情况十是否使用也不可知,感兴趣的可以研究下

    
    A库依赖B库为例,B库中有一个静态库libB.a :
    
    在A库中修改.podspec :
    
    s.pod_target_xcconfig = {
    
    'FRAMEWORK_SEARCH_PATHS'`=>'$(inherited) $(PODS_ROOT)/Crashlytics',
    
    'OTHER_LDFLAGS'         => '$(inherited) -undefined dynamic_lookup'
    
    }, 
    
    然后在Podfile中添加hook:
    
    pre_install do |installer|
    
    # workaround ``for` `https:``//github.com/CocoaPods/CocoaPods/issues/3289
    
    def installer.verify_no_static_framework_transitive_dependencies; end
    
    end
    

    新特性

    在pod1.5.0之后,安装包含swift第三方库的时候,不限制必须在podfile内声明use_frameworks!.但是,如果swift库依赖OC库,就需要在OC库内允许modular headers

    Modular Headers

    CocoaPods在创建之初,就致力于封装尽可能多的第三方库.pod管理了第三方库的头文件搜索路径(header search paths).pod允许任意pod之间的相互引用,不需要考虑命名空间,不用制定import <nameSpace/fileName>.

    例如B库使用#import"A.h"的,pod会配置对应的build setting来保证这种引入的可行.但是如果在其他库内增加了module maps,这种引用就会找不到文件.pod尝试自动去管理静态库的module maps,但是因为这样破坏了pod的使用方式,没有进行下去.
    说一下 module maps
    在XCode的build setting内,Packaging内有以下设置module map的选项

    1. Defines Module (DEFINES_MODULE) :
      如果设置为YES,会认为项目自定义自己的组件,允许项目通过组件的方式引入

    2. Module Map File (MODULEMAP_FILE)
      用来管理LLVM的module map,定义编译器组件结构.如果defines module为YES的时候,如果Module Map File没填,会自动生成.

    在pod1.5.0版本中,通过直接import和组件导入都能找到文件.对于pod开发者,可以在pod_target_xcconfig内添加'DEFINES_MODULE' => 'YES'.对于使用者,可以在podfile内添加use_modular_headers!允许直接import和module map.也可以通过:modular_headers => true配置特定的pod.

    引用

    1. cocoapods 1.4.0特性汇总
    2. pod关于static framework的支持
    3. swift官方网站
    4. 动态库会导致启动时间太长
    5. 动态库的个数不要多于6个
    6. library VS framework
    7. ios中的库
    8. pod关于swift版本的讨论
    9. swiftweekly,苹果支持Xcode 9 beta 4发布swift
    10. pod制定swift版本范围
    11. 静态库和动态库加载方式详解
    12. 苹果官方动态库文档
    13. cocoapods原理总结
    14. dylib浅析
    15. 动态库,在构建阶段,转为静态库加载的形式
    16. 组件化-动态库
    17. pod issue static library
    18. import
    19. [pod 1.5.0 不用use_frameworks!]([http://blog.cocoapods.org/CocoaPods-1.5.0/]

    相关文章

      网友评论

        本文标题:cocoapods的静态库和动态库

        本文链接:https://www.haomeiwen.com/subject/fznixftx.html