动态库与静态库

2016-07-22 » iOS开发基础

认识库

分清『 .framework 和 .a 』、『 动态库和静态库』、『 .tbd 和 .dylib』 等,.framework 不一定就是动态库

一、库(Library)

库是指一段编译好的二进制代码,并提供头文件供别人使用。

静态库(Static library)、动态库(Dynamic library)

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

2、静态库不能包含Swift文件,pod将第三方编译为static library,不能支持swift语言。新版的改为了framework的形式。

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

二、动态库(Dynamic library)

动态库也称动态链接,是在程序编译时不会被链接到目标代码中,直到可执行文件被执行过程中才被加载调用,即按需加载。

1、常见以 .tbd 或 .dylib 或 .framework 结尾。(默认在.app包内的Framework文件夹中)

2、动态库最低支持版本 8.0

3、动态库存储结构

当App依赖的第三方库采用动态库形式链接,编译器链接时,将第三方库相对引用,存储于App代码区域,真实的依赖资源存储于操作系统或各个App共用路径下。App启动时,只加载需要的资源到App寻址地址空间,即按需索取。其流程如下图

4、

App之间共用库,但是苹果本身是强制不允许的(进程间共享动态库是不允许的,iOS上的动态库只能是私有的,因此我们仍然不能将动态库文件放置在除了自身沙盒以外的其它任何地方。)

但iOS8之后,开放了App Extension功能,可以为一个应用创建插件,这样主app和插件之间共享动态库还是可行的。

5、

签名:动态库都有签名

比如我们在.app包里可以看到 _CodeSignature 会出现在很多包含非系统资源的目录级,这个其实就相当于资源签名,也是为了防止热更新等操作。

6、

OC和Swift 都是C编写的,编译后都是.o文件 ,为什么混编的时候不能用动态库,只是因为苹果不允许OC Swift混编成库而已。。。

7、

@import 和 #import的区别

#import 防止重复导入,
#import 相当于直接导入对应文件,而@import相当于引入一个对应文件的地址,需要使用的时候再去引入使用
@import 引入某个框架下得某个文件可以不需要明确框架,会自动引入。
#import 编译会慢一点

import 中得 <> 和 "" 的区别  查找路径优先级不同,<> 优先查找系统层再找用户自定义层,""相反 

动态库引入其他动态库的时候要用@import

对于相同路径的动态库,系统只会加载一次

import

动态库引用问题

当动态库引用动态库的时候,需要避免多层动态库循环引用,pod 工程帮我们处理了多个动态库的合并问题,当我们dependency的时候,出现的多次引入的库会被合并。相当于pod 把工程的引用关系都抽离到主工程,其余的每个pod都作为独立的framework引入,framework link 顺序根据引用顺序来。

pod主动把所有framework整理了,那互相的引用关系就全部靠主工程维系

当比如我们在pod动态库中再次引用 其他的三方打好的framework的时候, 就需要注意 引用传递的问题,pod希望把 三方库包含到自己的framework中,pod install通不过

两个framework都各自 把依赖的共同的 三方framework打入自己,造成引用传递


问题

1、动态库之间是否可以循环引入?

如果全部是动态库,那么最终都会落在Frameworks 中,动态库包含自己引入的动态库的references文件,按需加载。

2、一个动态库 引入另一个动态库framework 或者一个静态库

动态库打成Framework后 要独立给别的地方使用,这种情况下说明 这个时候每一行代码的调用,内存寻址就已经确定了。所以动态库中引入的内容需要已经在framework中?? 还是只是包含reference

运行时动态库link的时候,会根据自己依赖的动态库优先法则,先加载你依赖的动态库。然后再加载你自己

动态库引用静态库,需要把静态库打到自己的 Framework里面 还是 静态库会被打包到 主工程可执行文件里面?

按我们的处理,动态库无法包裹静态库,需要静态库包裹静态库,打到主工程可执行文件

三、静态库(Static library)

对于一个静态库而言,其实已经是编译好的了,类似一个 .o 的集合,在 build 的过程中只会参与链接的过程,而这个链接的过程简单的讲就是合并,并且链接器只会将静态库中被使用的部分合并到可执行文件 ( XXX.app 包内的exec 可执行文件 ) 中。

1、常见以.a或.framework结尾。

2、静态库存储结构

当App依赖的第三方库采用静态库形式链接,编译器链接时,将收集编译好代码和第三方库,存储于App代码区域,即这些资源是App可执行文件的一部分。App启动时,存储于App代码区域的代码将加载到App寻址地址空间。其流程如下图

####

四、动态库 VS 静态库

1、动态库和静态库的使用

这两个东西都是编译好的二进制文件。就是用法不同而已。

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

动态库只需要被系统加载一次。然后使用该库的工程只需要对其进行引用即可。 通过动态库链接时是通过类似静态库的形式被加载到程序中的,但是方法只是加载了方法声明。

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

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

2、library 和 framework

library 仅能包含编译后的代码,即.a文件,不能包含其他的资源文件,但是我们封装的第三方库,有时需要包含.h文件、.nib文件、图片、文档等。

framework 可以包含以上所有类型.且支持包含swift代码, 只支持iOS8以后,而static library可以追溯到iOS6.

由于 iOS 的沙盒机制,自己创建的 Framework 和系统Framework 不同,App 中使用的 Framework 在沙盒里,而不是系统中。每个 App 都只能用自己对应签名的动态库,做不到多个 App 使用一个动态库,APP extension可以共享。因此苹果又把这种 Framework 称为 Embedded Framework。

.framework VS .a

.a是一个纯二进制文件,不能直接拿来使用,需要配合头文件、资源文件一起使用。在 iOS 中是作为静态库的文件名后缀。

.framework中除了有二进制文件之外还有资源文件,可以拿来直接使用。

在不能开发动态库的时候,其实 『.framework = .a + .h + bundle』。而当 Xcode 6 出来以后,我们可以开发动态库后『.framework = 静态库/动态库 + .h + bundle』

.tbd VS .dylib

.dylib 就是动态库的文件的后缀名。
 
xcode 7.0 以后 导入系统提供的动态库 就不再有.dylib了 取而代之的就是 .tbd
 
而 .tbd(存储跨平台模块映射的文本文件) 其实是一个YAML本文文件,描述了需要链接的动态库的信息。主要目的是为了减少app 的下载大小

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

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

3、原理分析

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

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

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

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

目标文件:包含两种符号表:

1.文件转换后的符号(名称和方法的地址及偏移量)

2.未确定的符号(需要在链接阶段才能解析完成的机器代码)

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

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

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

  • 若符号来⾃静态库(本质就是.o 的集合包)或 .o,将其纳⼊链接产物,并确定符号地址
  • 若符号来⾃动态库,打个标记,等启动的时候再说—交给 dyld 去加载和链接符号

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

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

  • Static Loading:启动时
  • Dynamic Loading:启动后(使⽤时) Link 链接:决议符号地址
  • Static Linking:构建(链接)时
  • Dynamic Linking:运⾏时(启动时或使⽤时)

五、动态库 VS 静态库

1、搜索路径 (Search path)

搜索路径是指程序链接时,到哪个位置寻找库。

@executable_path:表示可执行程序所在的目录。
@loader_path:表示每一个被加载的 binary (包括App, dylib, framework, plugin等) 所在的目录。
@rpath:是一个保存着一个或多个路径的变量,告诉连接器到哪里找库。

2、Mach-O Type

全称 Mach Object Type ,是一种用于存储可执行文件,目标代码,动态库,内核转储的文件格式。

Xcode中查看Target 的 Build Settings 可以发现可以选择,里面就包含了动态库和静态库, 红色靶心图标的表示的就是无代码库,外部库。

在制作 framework 的时候需要选择这个 Mach-O Type.

3、Embedded VS. Linked

工程General中 有两个地方可以加库

1、Embedded Binaries

Embedded 的意思是嵌入,但是这个嵌入并不是嵌入 app 可执行文件,而是嵌入 app 的 bundle 文件。当一个 app 通过 Embedded 的方式嵌入一个 app 后,在打包之后解压 ipa 可以在包内看到一个 framework 的文件夹,下面都是与这个应用相关的动态framework

2、linded feameworks and libraries 

在 linded feameworks and libraries 这个下面我们可以连接系统的动态库、自己开发的静态库、自己开发的动态库。对于这里的静态库而言,会在编译链接阶段连接到app可执行文件中,而对这里的动态库而言,虽然不会链接到app可执行文件中,如果你不想在启动的时候加载动态库,可以在 linded feameworks and libraries 删除,并使用dlopen加载动态库。(dlopen 不是私有 api。)

- (void)dlopenLoad{
    NSString *documentsPath = [NSString stringWithFormat:@"%@/Documents/Dylib.framework/Dylib",NSHomeDirectory()];
    [self dlopenLoadDylibWithPath:documentsPath];
}

- (void)dlopenLoadDylibWithPath:(NSString *)path
{
    libHandle = NULL;
    libHandle = dlopen([path cStringUsingEncoding:NSUTF8StringEncoding], RTLD_NOW);
    if (libHandle == NULL) {
        char *error = dlerror();
        NSLog(@"dlopen error: %s", error);
    } else {
        NSLog(@"dlopen load framework success.");
    }
}

4、制作自己的Framework

iOS - 静态库的创建与使用

  在日常项目开发中,不论是为了两个公司项目上的业务交流还是为了减少项目的编译时间,有的时候我们会把项目中的私密内容打包成静态库,或者是把项目中变动较少一部分打包成静态库以便提高编译效率,那么下面我们就来学习一下”iOS-静态库的创建与使用”:

(一)iOS静态库、动态库与Framework静态库与动态库的区别:

  (1)什么是库?

    库(Library)直白一点说就是一段编译好的二进制代码,加上头文件就可以供别人使用;(例如: iOS中Objective-C编译下的.h和.m文件,打包静态库后会变为.h和.a文件);

  (2)什么是静态库?

    ①静态库即静态链接库(例如: windows下的.lib、Mac和Linux下的.a);

    ②静态库在编译的时候会被直接拷贝一份,复制到目标程序里并且这段代码在目标程序里就不会在改变了,我猜这也是该库叫”静态库”的原因;

    ③静态库的利弊:

      1)利: 静态库在编译完成之后,库文件实际上就没有作用了,目标程序没有外部依赖,直接就可以运行;

      2)弊: 弊端静态库会使用目标程序的体积增大;

  (3)什么是动态库?

    ①动态库即动态链接库(例如: windows下的.dll、Mac下的.dylib、Linux下的.so);

    ②与静态库相反,动态库在编译时并不会被拷贝到目标程序中,目标程序中只会存储指向动态库的引用,等到程序运行时,动态库才会被真正加载进来;

    ③动态库的利弊:

      1)利: 不需要拷贝到目标程序中,不会影响目标程序的体积,而且同一份库可以被多个程序使用(因为这个原因,动态库也被称作共享库); 同时,编译时才载入的特性,也可以让我们随时对库进行替换,而不需要重新编译代码;

      2)弊: 动态载入会带来一部分性能损失,使用动态库也会使得程序依赖于外部环境;如果环境缺少动态库或者库的版本不正确,就会导致程序无法运行;

  (4)什么时候我们会用到库呢?

    ①某些代码片段需要给别人使用,但是我们不希望别人看到源码,就需要以库的形式进行封装,只暴露出头文件;

    ②对于某些不会进行大的改动的代码,我们想减少编译的时间,就可以把它打包成库,因为库是已经编译好的二进制了,编译的时候只需要 Link 一下,不会浪费编译时间;

    说明:上面提到库在使用的时候需要Link,Link 的方式有两种:静态和动态,于是便产生了静态库和动态库(“攻城狮”的思维就是这么简单😄😄);

  (5)iOS Framework?

    ①除了上面我们提到iOS的.a和.dylib之外,Mac OS/iOS 平台还可以使用 Framework,Framework实际上是一种打包方式,将库的二进制文件、头文件和有关的资源文件打包到一起,方便管理和分发;在 iOS 8 之前,iOS 平台不支持使用动态 Framework,开发者可以使用的 Framework 只有苹果自家的 UIKit.Framework,Foundation.Framework 等等;

    ②上面这种限制可能是出于安全的考虑,换一个角度讲,因为 iOS 应用都是运行在沙盒当中,不同的程序之间不能共享代码,同时动态下载代码又是被苹果明令禁止的,没办法发挥出动态库的优势,实际上动态库也就没有存在的必要了;

    ③由于上面提到的限制,开发者想要在 iOS 平台共享代码,唯一的选择就是打包成静态库 .a 文件,同时附上头文件;但是这样的打包方式不够方便,使用时也比较麻烦,大家还是希望共享代码都能能像 Framework 一样,直接扔到工程里就可以用;

    ④终于在日思夜盼便迎来了iOS对动态库的支持: iOS 8/Xcode 6 推出之后,iOS 平台添加了动态库的支持,同时 Xcode 6 也原生自带了 Framework 支持(动态和静态都可以);

(二)切入主题”iOS静态库的创建”:

  (1)我们先了解静态库文件的版本(四种):

    ①真机-Debug版本;  ②真机-Release版本  ③模拟器-Debug版本  ④模拟器-Release版本;

  (2)Debug(调试)版本特点:

    ①含完整的符号信息,以方便调试;  ②不会对代码进行优化;

  (3)Release(发布)版本特点:

    ①不会包含完整的符号信息;  ②执行代码是进行过优化的;  ③大小会比Debug版本的略小  ④在执行速度方面,Release版本会快一些;

    所以我们一般开发中都打包Release(发布)版本使用;

  (4)再来了解一下iPhone设备CPU的架构简介:

    ①模拟器: (4s~5: i386)    (5s~6splus: x86_64)

    ②真 机: (3gs~4s: armv7)  (5~5c: armv7s)     (5s~6splus: arm64)[说明: 静态库只要支持了armv7,就可以跑在armv7s的架构上];

http://www.cnblogs.com/dingding3w/p/5338341.html

1、工程由静态库转为动态库,所有pod资源需要跟着改变,pod原先使用的resources_bundle打资源包的方式改变

统一如下:
1、把framework 本身作为一个工程对待,资源文件直接放置在framework中
目录大致如下:
_CodeSignature
Assets.car (图片 Images.xcassets 资源)
xxxx.nib
Info.plist
可执行文件
等等其他文件类型

图片资源调用方式:
UIImage(named: <#T##String#>, in: <#T##Bundle?#>, compatibleWith: <#T##UITraitCollection?#>)

2、Swift pod 库的建立与使用

1、
swift 工程 引用 Swift pod库 

Swift写的 pod 中遇到的错误:import 了对应的 module后还是提示未定义

Swift写的需要暴露的部分需要使用public 或者 open修饰,所有需要暴露出去的内容都需要修饰,否则默认其他module是无法使用的

public: 只能被本module内容 继承重写
open: 可以被其他module内容 继承重写

2、swift工程引用OC pod库  不需要通过桥文件 直接import对应的库名称

http://www.cocoachina.com/ios/20141126/10322.html

https://www.cnblogs.com/breezemist/p/5513036.html

https://www.jianshu.com/p/71b5ecacaaac

https://www.jianshu.com/p/cbec1da24585

Could not build Objective-C module ‘keyword’

framework生成问题

dyld: Library not loaded: 各种情况

第一种情况:

dyld: Library not loaded: @rpath/libswiftCore.dylib

Referenced from: /var/containers/Bundle/Application/CF227EE4-F36F-4161-A8A4-BB063D74B0CF/Boss.app/Boss

Reason: no suitable image found.  Did find:

/private/var/containers/Bundle/Application/CF227EE4-F36F-4161-A8A4-BB063D74B0CF/Boss.app/Frameworks/libswiftCore.dylib: code signature invalid for '/private/var/containers/Bundle/Application/CF227EE4-F36F-4161-A8A4-BB063D74B0CF/Boss.app/Frameworks/libswiftCore.dylib'
***带有 Swift 项目 ***
解决办法:

1.rm -rf "$(getconf DARWIN_USER_CACHE_DIR)/org.llvm.clang/ModuleCache"

2.rm -rf ~/Library/Developer/Xcode/DerivedData

3.rm -rf ~/Library/Caches/com.apple.dt.Xcode
第二种情况
dyld: Library not loaded: @rpath/Charts.framework/Charts

Referenced from: /var/containers/Bundle/Application/FEFF646B-5902-4015-B159-C141EB0E8DC0/test.app/test

Reason: image not found
动态库未链接到项目内
解决办法:

在 TARGETS —> General —> Embedded Binaries 下,点击 + 按钮,选择 Charts.framework就可以解决问题
第三种情况(一般出现在真机)

dyld: Library not loaded: @rpath/Charts.framework/Charts

..........

code signing blocked mmap() of

............
***证书有问题  ***
解决办法:

把所有证书删掉 重新安装





合并SDK打开终端,cd 到 ‘~/Desktop/framework_sdk/’文件夹下执行命令,本例为:命令:lipo -create s_MyFramework.framework/MyFramework i_MyFramework.framework/MyFramework -output Myframework


​ 重要一步:随便复制一个framework,比如本例:“i_MyFramework.framework” 将其改名为“MyFramework.framework”然后将“MyFramework.framework”包里的“MyFramework”替换成合并后的“Myframework”.

cocoapod + ruby 脚本配合自动制作 framework

参考

iOS 开发中的『库』