精华内容
参与话题
问答
  • 动态库和静态库的区别

    千次阅读 2016-06-29 09:21:43
    一、什么是库? 库是共享程序代码的方式,一般分为静态库和动态库。...二、静态库和动态库的好处 使用静态库的好处: 1、模块化,分工合作 2、避免少量改动经常导致大量的重复编译连接 3、也可以重用,注意
    一、什么是库?
    库是共享程序代码的方式,一般分为静态库和动态库。
    静态库:链接时完整地拷贝至可执行文件中,被多次使用就有多份冗余拷贝。

    动态库:链接时不复制,程序运行时由系统动态加载到内存,供程序调用,系统只加载一次,多个程序共用,节省内存。

    二、静态库和动态库的好处

    使用静态库的好处:

    1模块化,分工合作
    2避免少量改动经常导致大量的重复编译连接
    3也可以重用,注意不是共享使用
    动态库使用有如下好处:
    1使用动态库,可以将最终可执行文件体积缩小
    2使用动态库,多个应用程序共享内存中得同一份库文件,节省资源
    3使用动态库,可以不重新编译连接可执行程序的前提下,更新动态库文件达到更新应用程序的目的。
    从1可以得出,将整个应用程序分模块,团队合作,进行分工,影响比较小。
    从2可以看出,其实动态库应该叫共享库,那么从这个意义上来说,苹果禁止iOS开发中使用动态库就可以理解了:
    因为在现在的iPhone,iPodTouch,iPad上面程序都是单进程的,也就是某一时刻只有一个进程在运行,那么你写个共享库,
            ----共享给谁?(你使用的时候只有你一个应用程序存在,其他的应该被挂起了,即便是可以同时多个进程运行,别人能使用你的共享库里的东西吗?你这个是给你自己的程序定制的。)
            ----目前苹果的AppStore不支持模块更新,无法更新某个单独文件(除非自己写一个更新机制:有自己的服务端放置最新动态库文件)

    至于苹果为啥禁止ios开发使用动态库我就猜到上面俩原因

    三、iOS里静态库和动态库形式?
    静态库:.a和.framework
    动态库:.dylib和.framework
    四、framework为什么既是静态库又是动态库?
    系统的.framework是动态库,我们自己建立的.framework是静态库。
    五、a与.framework有什么区别?
    .a是一个纯二进制文件,.framework中除了有二进制文件之外还有资源文件。
    .a文件不能直接使用,至少要有.h文件配合,.framework文件可以直接使用。
    .a + .h + sourceFile = .framework。
    建议用.framework.
    六、为什么要使用静态库?
    方便共享代码,便于合理使用。
    实现iOS程序的模块化。可以把固定的业务模块化成静态库。
    和别人分享你的代码库,但不想让别人看到你代码的实现。
    开发第三方sdk的需要。
    七、制作静态库时的几点注意:
    1 注意理解:无论是.a静态库还.framework静态库,我们需要的都是二进制文件+.h+其它资源文件的形式,不同的是,.a本身就是二进制文件,需要我们自己配上.h和其它文件才能使用,而.framework本身已经包含了.h和其它文件,可以直接使用。
    2 图片资源的处理:两种静态库,一般都是把图片文件单独的放在一个.bundle文件中,一般.bundle的名字和.a或.framework的名字相同。.bundle文件很好弄,新建一个文件夹,把它改名为.bundle就可以了,右键,显示包内容可以向其中添加图片资源。
    3 category是我们实际开发项目中经常用到的,把category打成静态库是没有问题的,但是在用这个静态库的工程中,调用category中的方法时会有找不到该方法的运行时错误(selector not recognized),解决办法是:在使用静态库的工程中配置other linker flags的值为-ObjC。
    4 如果一个静态库很复杂,需要暴露的.h比较多的话,就可以在静态库的内部创建一个.h文件(一般这个.h文件的名字和静态库的名字相同),然后把所有需要暴露出来的.h文件都集中放在这个.h文件中,而那些原本需要暴露的.h都不需要再暴露了,只需要把.h暴露出来就可以了。


    展开全文
  • 静态库和动态库的区别 举个例子, iOS 项目中使用 Embeded Framework 静态库和动态库如何构建和加载 静态库和动态库依赖关系 Xcode 项目结构 iOS 依赖管理事实上的标准 解决...
    • 起因

    • 理论功底

      • 动态库和静态库

        • 介绍

        • 静态库和动态库的区别

        • 举个例子, iOS 项目中使用 Embeded Framework

        • 静态库和动态库如何构建和加载

        • 静态库和动态库依赖关系

      • Xcode 项目结构

        • iOS 依赖管理事实上的标准

      • 解决问题

        • 制作动态库

      • 剖析下动态库 Framework 吧

        • 回过头来看 Embened Framework

        • Why Swift does not Support Staic Libraies

        • CocoaPods 使用 Use_framework!

        • 动态库 Framework 的文件结构

          • 更愉快的导入文件

            资源问题

    • 参考

    起因

    去年,公司iOS端,之前由于所有的业务端代码都是混乱管理,造成开发有很多痛点无法单测,团队成员提交代码冲突机率大,CI配合效果差,功能性代码多端无法复用,单仓库代码量大,编译时间长 等等痛点,领导和组内多次沟通开始着手组件化开发,希望能改进这些开发中的痛点,成立组件化团队。

    组件化的方案大同小异,基础性代码封装私有库,业务组件交互交由中间件负责,项目依赖工具用iOS项目事实上的标准CocoaPods

    前期的基础性组件拆分都较为顺利,从依赖树的叶子节点开发是最合适的方案。

    随着组件抽离的越来越多,私有库的依赖体系也越来越复杂,慢慢过渡到了业务组件。业务组件用了Swift的第三方组件,用了Swift库的同学都知道必须加上use_frameworks!,这个标记是说Pod管理的依赖全部编译为动态库,然后呢我们的很多组件又依赖了诸如百度地图,微信分享等静态库,于是我在执行 pod install 报了一个没有碰见过的错误。

    1
    [!] The 'Pods-LJA_Example' target has transitive dependencies that include static binaries:

    installError.png

    这就尴尬了,于是一阵疯狂的搜索google stackoverflow等,然而并没有什么卵用,而且上面催得急,根本没时间处理这些小问题 业务重构是最主要的,以至于我们的业务组件没有做到独立仓库拆分。

    直到最近终于找到了解决办法:( 主要是自己的功力不够深厚)

    理论功底

    动态库和静态库

    介绍

    首先静态库和动态库都是以二进制提供代码复用的代码库

    • 静态库 常见的是 .a

    • 动态库常见的是 .dll(windows),.dylib(mac),so(linux)

    • framework(in Apple): Framework 是Cocoa/Cocoa Touch程序中使用的一种资源打包方式,可以将代码文件、头文件、资源文件、说明文档等集中在一起,方便开发者使用。也就是说我们的 framework其实是资源打包的方式,和静态库动态库的本质是没有关系的

    静态库和动态库的区别

    静态库: 链接时会被完整的复制到可执行文件中,所以如果两个程序都用了某个静态库,那么每个二进制可执行文件里面其实都含有这份静态库的代码

    动态库: 链接时不复制,在程序启动后用dyld加载,然后再决议符号,所以理论上动态库只用存在一份,好多个程序都可以动态链接到这个动态库上面,达到了节省内存(不是磁盘是内存中只有一份动态库),还有另外一个好处,由于动态库并不绑定到可执行程序上,所以我们想升级这个动态库就很容易,windows和linux上面一般插件和模块机制都是这样实现的。

    But我们的苹果爸爸在iOS平台上规定不允许存在动态库,并且所有的 IPA 都需要经过苹果爸爸的私钥加密后才能用,基本你用了动态库也会因为签名不对无法加载,(越狱和非 APP store 除外)。于是就把开发者自己开发动态库掐死在幻想中。

    直到有一天,苹果爸爸的iOS升级到了8,iOS出现了APP Extension,swift编程语言也诞生了,由于iOS主APP需要和Extension共享代码,Swift语言的机制也只能有动态库,于是苹果爸爸尴尬了,不过这难不倒我们的苹果爸爸,毕竟我是爸爸,规则是我来定,我想怎样就怎样,于是提出了一个概念Embedded Framework,这种动态库允许APP和APP Extension共享代码,但是这份动态库的生命被限定在一个APP进程内。简单点可以理解为被阉割的动态库。

    举个例子,iOS项目中使用Embeded Framework

    如果你把某个自己开发的动态库(系统的不算,毕竟苹果是爸爸)放在了Linked Frameworks and Libraries里面,程序一启动就会报Reason: Image Not Found,你只能把它放在Embeded Binaries里面才能正常使用,

    看图: 

    useEmbededFramework.png

    静态库和动态库如何构建和加载

    简单点,说话的方式简单点~~

    上面的介绍貌似有点抽象啊套用在美团技术分享大会上的话就是:

    • 静态库: 一堆目标文件(.o/.obj)的打包体(并非二进制文件)

    • 动态库: 一个没有main函数的可执行文件

    这里我们来复习下C语言的基本功,编译和链接

    • 编译: 将我们的源代码文件编译为目标文件

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

    由于某个目标文件的符号(可以理解为变量,函数等)可能来自其他目标文件,其实链接这一步最主要的操作就是决议符号的地址。

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

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

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

    • Load 装载:将库文件载入内存

        Static Loading:启动时

        Dynamic Loading:启动后(使用时)

    • Link 链接:决议符号地址

        Static Linking:构建(链接)时

        Dynamic Linking:运行时(启动时或使用时)

    然后组合起来就是 2 * 2 = 4 了

    • Static Loading + Static Linking

    • Static Loading + Dynamic Linking

    • Dynamic Loading + Dynamic Linking

    • ~~Dynamic Loading + Static Linking~~

    第一种是纯静态库相关了

    第二种就是静态加载(启动时),动态链接,链接时,动态库参与链接,但是这时候只是给符号打了标记告诉我这个符号来自与动态库,程序启动时,iOS或者Mac OS操作系统的dyld自动load + link。

    既然全部都是自动的。那么符号的调用方完全不知道你到底是源码还是静态库,动态库 。

    第三种收到调用dlopen + performSelector通常iOS的APP不适用这里不讨论

    第四种,没见过,个人也不是特别懂

    有需求请参看文后的程序员的自我修养一书

    静态库和动态库依赖关系

    既然有 2 种库,那么依赖关系又是 2 * 2 喽

    • libA.a dependency libB.a

    • UIKit.dylib dependency Foundation.dylib

    • libA.a dependency Foundation.dylib

    • MyXX.dylib dependency libA.a

    第一种 静态库互相依赖,这种情况非常常见,制作静态库的时候只需要有被依赖的静态库头文件在就能编译出来。但是这就意味者你要收到告诉使用者你的依赖关系

    幸运的是CocoaPod就是这样做的

    第二种动态库依赖动态库,两个动态库是相互隔离的具有隔离性,但是制作的静态库的时候需要被依赖动态库参与链接,但是具体的符号决议交给dyld来做。

    第三种,静态库依赖动态库,也很常见,静态库制作的时候也需要动态库参与链接,但是符号的决议交给dyld来做。

    第四种,动态库依赖静态库,这种情况就有点特殊了。首先我们设想动态库编译的时候需要静态库参与编译,但是静态库交由dyld来做符号决议,but这和我们前面说的就矛盾了啊。静态库本质是一堆.o 的打包体,首先并不是二进制可执行文件,再者你无法保证主程序把静态库参与链接共同生成二进制可执行文件。这就尴尬了。

    怎么办?

    目前的编译器的解决办法是,首先我无法保证主程序是否包含静态库,再者静态库也无法被dyld加载,那么我直接把你静态库的.o 偷过来,共同组成一个新的二进制。也被称做吸附性

    那么我有多份动态库都依赖同样的静态库,这就尴尬了,每个动态库为了保证自己的正确性会把静态库吸附进来。然后两个库包含了同样的静态库,于是问题就出现了。 看到这里想必前面出现的错误你已经能猜出来了把~_~

    后面再详细解释

    先来个总结

    可执文件(主程序或者动态库)在构建的链接阶段

    • 遇到静态库,吸附进来

    • 遇到动态库,打标记,彼此保持独

    Xcode 项目结构

    • target:对于一个产物(app,.a ,.framework)

    • project:一个项目包含多个 target

    • workspace: 一个包含多个 target

    • schema: 指定了一个产物是按照何种的依赖关系,编译-链接到最终的一个产物

    iOS 依赖管理事实上的标准

    这么多年,Apple的博客和文档也就告诉了我们什么是静态库什么是动态库,如何制作等。但是并没有给我们提供一系列的依赖管理工具。所以CocoaPods成了事实上的标准。

    通常CocoaPods管理的工程结构如下:

    QQ截图20170427095936.png

    那么当我们按下CMD + B的时候,整个项目按照先编译被依赖Pod,然后依赖其他Pod的Pod也被构建出来,最终所有的组件被编译为一个lib-Pods-XXXAPP.a被添加进项目进去。资源通过CocoaPods提供的脚本也一并被复制进去。想了解CocoaPods做了什么的读者可以参看后面的链接

    解决问题

    这么多理论功底的建立,相信我们已经能分析出来之前pod install的原因了。就是用了use_framework那么我们的所有Pod都会以动态库(Embeded Framework)的形式去构建,于是那些非开源的库(如百度地图,微信分享)如果被多个Pod依赖(组件化开发中太常见了)于是被吸附到动态库里面,所以CocoaPod直接就不让我们install成功。因为你现在的依赖管理就是错误的。

    在听取美团叶樉老师分享的时候 他们的出发点是因为要绕过苹果爸爸在iOS9以下对__text 段60M的限制使用了动态库方案,我们是因为某些swift库必须要用到(历史遗留原因)动态库。美团的做法是摘除依赖关系,自定义CocoaPods(开源的本来就是用着不爽我就改)。但是我是个小菜鸡啊。我也不会 ruby(以后会学的),但是叶樉老师给我提了别的idea。前面我们知道 动态库和动态库是隔离性,动态库依赖静态库具有吸附性,那么我们可以自定义一个动态库把百度地图这种静态库吸附进来。对外整体呈现的是动态库特性。其他的组件依赖我们自定义的动态库,由于隔离性的存在,不会出现问题。

    制作动态库

    1 创建动态库项目这里以 wx 举例

    createDynamicFramework.png

    2 按照微信的官方文档。添加依赖库(我是因为pod install巨慢所以我直接拽进来了)

    wxDdependency.png

    3 将wx的PublicHeader暴露出来,注意由于我并没有使用到wx相关API所以链接器帮我们链接动态库的时候可能并不会把wx静态库吸附进来。我们手动在build Setting的other link flags加上-all_load标记

    publicHeader.png

    4.在Schema里面跳转编译配置为Release,并且选择所有的CPU架构

    SchemaRelease.png

    buildArchive.png

    5 然后选择模拟器或者 Generic iOS Device 运行编译就会生成对应版本的 Framework 了。

    releaseFrameworl.png

    6.但是为了保证开发者使用的时候是真机模拟器都能正常使用,我们需要合并不同架构

    这里在Build Phases里添加以下脚本,真机和模拟器都Build一遍之后就会在工程目录下生成Products文件夹,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    if "${ACTION}" "build" ]
    then
    INSTALL_DIR=${SRCROOT}/Products/${PROJECT_NAME}.framework
    DEVICE_DIR=${BUILD_ROOT}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework
    SIMULATOR_DIR=${BUILD_ROOT}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework
    if [ -d "${INSTALL_DIR}" ]
    then
    rm -rf "${INSTALL_DIR}"
    fi
    mkdir -p "${INSTALL_DIR}"
    cp -R "${DEVICE_DIR}/" "${INSTALL_DIR}/"
    #ditto "${DEVICE_DIR}/Headers" "${INSTALL_DIR}/Headers"
    lipo -create "${DEVICE_DIR}/${PROJECT_NAME}" "${SIMULATOR_DIR}/${PROJECT_NAME}" -output "${INSTALL_DIR}/${PROJECT_NAME}"
    open "${DEVICE_DIR}"
    open "${SRCROOT}/Products"
    fi

    fatframework.png

    于是我们有了我们自己的私有动态库LJWXSDK,那么我们来验证我们之前的问题

    首先指定一个LJWXSDK.podspec这里我直接传到了我的Github上面

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    #
    # Be sure to run `pod lib lint LJPod.podspec' to ensure this is a
    # valid spec before submitting.
    #
    # Any lines starting with a # are optional, but their use is encouraged
    # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html
    #
    Pod:: Spec.new do |s|
      s.name             = 'LJWXSDK'
      s.version          = '0.1.0'
      s.summary          = 'A short description of LJWXSDK.'
      s.description      = < 'MIT', : file => 'LICENSE' }
      s.author           = { 'ValiantCat' => '519224747@qq.com' }
      s.source = { : http  => 'http://onk2m6gtu.bkt.clouddn.com/LJWXSDK.framework.zip' }
      s.ios.deployment_target = '8.0'
      s.default_subspec = 'zip'
      s.subspec 'zip' do |zip|
       
        puts '-------------------------------------------------------------------'
        puts 'Notice: LJWXSDK is zip now'
        puts '-------------------------------------------------------------------'
        zip.ios.vendored_frameworks = '*.framework'
      end
    end

    注意上面我是把二进制压缩丢进了七牛的 oss 文件存储。毕竟免费还快。

    然后通过 pod lib create 创建了一个 pod 用来验证之前我们的传递性依赖问题,

    文件夹结构如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    .
    ├── Example
    │   ├── LJA
    │   │   ├── Base.lproj
    │   │   │   ├── LaunchScreen.storyboard
    │   │   │   └── Main.storyboard
    │   │   ├── Images.xcassets
    │   │   │   └── AppIcon.appiconset
    │   │   │       └── Contents.json
    │   │   ├── LJA-Info.plist
    │   │   ├── LJA-Prefix.pch
    │   │   ├── LJAppDelegate.h
    │   │   ├── LJAppDelegate.m
    │   │   ├── LJViewController.h
    │   │   ├── LJViewController.m
    │   │   ├── en.lproj
    │   │   │   └── InfoPlist.strings
    │   │   └── main.m
    │   ├── LJA.xcodeproj
    │   ├── LJA.xcworkspace
    │   ├── Podfile
    │   ├── Podfile.lock
    │   ├── Pods
    │   │   ├── Headers
    │   │   ├── LJWXSDK
    │   │   │   └── LJWXSDK.framework
    │   │   │       ├── Headers
    │   │   │       │   ├── LJWXSDK.h
    │   │   │       │   ├── WXApi.h
    │   │   │       │   ├── WXApiObject.h
    │   │   │       │   └── WechatAuthSDK.h
    │   │   │       ├── Info.plist
    │   │   │       ├── LJWXSDK
    │   │   │       ├── Modules
    │   │   │       │   └── module.modulemap
    │   │   │       ├── _CodeSignature
    │   │   │       │   └── CodeResources
    │   │   │       └── read_me.txt
    │   │   ├── Local\ Podspecs
    │   │   │   ├── LJA.podspec.json
    │   │   │   ├── LJB.podspec.json
    │   │   │   └── LJWXSDK.podspec.json
    │   │   ├── Manifest.lock
    │   │   ├── Pods.xcodeproj
    │   │   │   ├── project.pbxproj
    │   │   │   ├── project.xcworkspace
    │   │   ├── Target\ Support\ Files
    │   │   │   ├── LJA
    │   │   │   │   ├── Info.plist
    │   │   │   │   ├── LJA-dummy.m
    │   │   │   │   ├── LJA-prefix.pch
    │   │   │   │   ├── LJA-umbrella.h
    │   │   │   │   ├── LJA.modulemap
    │   │   │   │   └── LJA.xcconfig
    │   │   │   ├── LJB
    │   │   │   │   ├── Info.plist
    │   │   │   │   ├── LJB-dummy.m
    │   │   │   │   ├── LJB-prefix.pch
    │   │   │   │   ├── LJB-umbrella.h
    │   │   │   │   ├── LJB.modulemap
    │   │   │   │   └── LJB.xcconfig
    │   │   │   ├── Pods-LJA_Example
    │   │   │   │   ├── Info.plist
    │   │   │   │   ├── Pods-LJA_Example-acknowledgements.markdown
    │   │   │   │   ├── Pods-LJA_Example-acknowledgements.plist
    │   │   │   │   ├── Pods-LJA_Example-dummy.m
    │   │   │   │   ├── Pods-LJA_Example-frameworks.sh
    │   │   │   │   ├── Pods-LJA_Example-resources.sh
    │   │   │   │   ├── Pods-LJA_Example-umbrella.h
    │   │   │   │   ├── Pods-LJA_Example.debug.xcconfig
    │   │   │   │   ├── Pods-LJA_Example.modulemap
    │   │   │   │   └── Pods-LJA_Example.release.xcconfig
    │   │   │   └── Pods-LJA_Tests
    │   │   │       ├── Info.plist
    │   │   │       ├── Pods-LJA_Tests-acknowledgements.markdown
    │   │   │       ├── Pods-LJA_Tests-acknowledgements.plist
    │   │   │       ├── Pods-LJA_Tests-dummy.m
    │   │   │       ├── Pods-LJA_Tests-frameworks.sh
    │   │   │       ├── Pods-LJA_Tests-resources.sh
    │   │   │       ├── Pods-LJA_Tests-umbrella.h
    │   │   │       ├── Pods-LJA_Tests.debug.xcconfig
    │   │   │       ├── Pods-LJA_Tests.modulemap
    │   │   │       └── Pods-LJA_Tests.release.xcconfig
    │   │   └── libWeChatSDK
    │   │       ├── README.md
    │   │       ├── WXApi.h
    │   │       ├── WXApiObject.h
    │   │       ├── WechatAuthSDK.h
    │   │       └── libWeChatSDK.a
    ├── LICENSE
    ├── LJA
    │   ├── Assets
    │   └── Classes
    │       └── LJA.m
    ├── LJA.podspec
    ├── LJB
    │   ├── Assets
    │   └── Classes
    │       └── LJB.m
    ├── LJB.podspec
    ├── README.md
    └── _Pods.xcodeproj -> Example/Pods/Pods.xcodeproj

    testframework.pngdependencyDynamicLibrary.png

    useDynamicFramework.png

    测试工程我也丢在7牛上面。下载测试即可

    编译运行。完美。我们又可以愉快的和swift第三方库配合使用。

    很多人可能会问 诸如百度地图 微信这种sdk为什么官方不支持动态库版(所说的都是embeded Framework),猜测是为了兼容更低iOS7版本吧

    很多人会觉得麻烦的要死。首先每个公司多多少少都有历史包袱,麻烦也要做,再者这是一次对基本功的补充,即便你们没有用到,但是为了学习,这篇教程所做的也值得你尝试一次。

    剖析下动态库 Framework 吧

    上述解决了我们一开始遇到的问题。but既然动态库和静态库压根就不一回事,所以里面还是有很多细节值得我们去了解的。

    回过头来看 Embened Framework

    首先我们之前记得如果一个动态库加在LinkedFrameworksand Libraies程序启动就会报ImageNotFound,如果放在EmbededBinaries里面就可以。这是为什么呢。我们拿MacoView来看下两种情况下可执行文件的细节

    loadEmbededFramework.png

    loadSystemFramework.png

    appContentFiles.png

    其中@rpth 这个路径表示的位置可以查看Xcode中的链接路径问题

    这样我们就知道了其实加在EmbededBinaries里面的东西其实会被复制一份到xx.app里面,所以这个名字起得还是不错的直译就是嵌入的框架

    Why Swift does not Support Staic Libraies

    造成这个的主要原因是Swift的运行时库(不等同于OC的runtime概念),由于Swift的ABI不稳定,静态库会导致最终的目标程序中包含重复的运行库,相关可以看下最后的参考文章SwiftInFlux#static-libraries。等到我们的SwiftABI稳定之后,我们的静态库支持可能就又会出现了。当然也可能不出Swift伴随诞生的SPM(Swift,Package Manager),可能有更好的官方的包依赖管理工具。让我们期待吧。

    CocoaPods使用Use_framework!

    既然加了Swift的第三方库之后就需要在Podfile里面加上use_framework! 那么CocoaPods就会帮我们生成动态库,但是奇怪的是,我们并没有在主工程的embeded binaries看到这个动态库,这又是什么鬼。其实是CocoaPods使用脚本帮我们加进去了。脚本位置在主工程的build Phase下的Emded Pods frameworks

    1
    "${SRCROOT}/Pods/Target Support Files/Pods-LJA_Example/Pods-LJA_Example-frameworks.sh"

    动态库Framework的文件结构

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    .
    ├── Headers
    │   ├── LJWXSDK.h
    │   ├── WXApi.h
    │   ├── WXApiObject.h
    │   └── WechatAuthSDK.h
    ├── Info.plist
    ├── LJWXSDK
    ├── Modules
    │   └── module.modulemap
    └── _CodeSignature
        └── CodeResources
    • Headers 一般是头文件。非private里面的头文件都会在里面

    • info.plist 配置信息,不深究

    • Modules 这个文件夹里有个module.modulemap文件,后面在讲解

    • 二进制文件,这就是上面提到的不带main的二进制文件了,.o 的打包体

    • _codeSignature签名文件 (苹果爸爸的约束)

    • more资源文件。这里暂时没用到,所以没有 ,但是这个也是个大坑

    更愉快的导入文件

    • @class,@protocol:不说了就是声明一个类,并不导入。

    • #import <>, #import"":是加强版的#include<>,#include"" 防止重复导入的。

    • #import<> : 通过build setting里面中的header Search Path里面去找

    • #import"" : 第一步先搜索user Header search Path再搜索 header search Path 。所以对我们的framework来说,CocoaPod帮我们加到了Header search Path目前2种导入方式都是可以支持的。

    上面的导入方式都带了 某个framework的路径"xx/xx.h" ,我们在开发自己主工程的时候会发现我们导入主工程其他类是不需要导入前缀的。 这又是怎么回事。

    看下面的配置

    no-recursive.png

    目前的配置是non-recursive。如果把non去掉意思就是我可以递归的去查找某些framework下面的头文件了。 但是Xcode的效率肯定就会有影响。

    还是不建议修改的好。

    大家都知道iOS7之后多了@import,这又是什么鬼。

    简单理解这个方式叫做Module导入,好处就是使用了@import 之后不需要在project setting手动添加framework,系统会自动加载,而且效率更高。

    最主要的是swift也只能这样用。

    导入的时候系统会查找如果有模块同名的文件就会导入这个文件。如果没有CocoaPods帮我们生成一个module-umbrela.hl文件,然后就是导入的这个文件。

    回过头来看我们的framework的结构 里面有个Modules文件夹,里面有个文件module.modulemap

    1
    2
    3
    4
    5
    6
    framework module LJWXSDK {
      umbrella header "LJWXSDK.h"
     
      export *
      module * { export * }
    }

    我们可以看到其实被暴露的header就是这个文件,之前我在按照#import "/"的时候有个警告

    missSubModule.png

    而且按照@import 导入的东西发现没有导入可用的头文件就是因为并没有在umbrella header的头文件中加入其他头文件。

    加入之后我们就可以完美的使用@import ,并且#import"/" 也不会报warning

    更多关于umbrella Header 参看文后参考

    资源问题

    首先我们来看常见的资源文件: 主要分为图片和其他类资源那么加载图片和加载其他资源都是怎么做的?

    1: [UIimage imageNamed:]

    2: [NSbundle bundleForclass[XXX class]]

    其实方式1去本质就是去mainBundle去拿资源,方式2从XXX所在的框架里面去拿。

    前面也说道framework只是资源的打包方式,本质上是有两种的。

    我们这个framework如果本质是静态库,那么无需改变使用方式,资源最终都会打包到Main Bundle里面

    如果我们这个framework本质是动态库,那么我们的资源就发生了变化,资源就会被存放在framework里面。所以我们需要使[NSbundle bundleForclass[XXX class]]。需要注意的是很多人为了简单,下意

    的使用self class传递,但是有可能这个self实例不在资源所属的framework。所以会出现资源加载失败。一定要谨慎使用。

    展开全文
  • 静态库和动态库的区别

    千次阅读 2013-10-15 08:52:11
    注意:Linux 静态编译时将动态库也编入文件中。 文件预览 文件目录树如下,如你所见,非常简单。  1. libtest/  2. |-- lt.c  3. |-- lt.h  4. `-- test.c 代码 #lt.c  1. /* lt.c  2. *  3. */  4....

    注意:Linux 静态编译时将动态库也编入文件中。

    文件预览
    文件目录树如下,如你所见,非常简单。
       1. libtest/
       2. |-- lt.c
       3. |-- lt.h
       4. `-- test.c
    代码
    #lt.c
       1. /* lt.c
       2. *
       3. */
       4.  
       5. #include  
       6.  
       7. void myprint(void)
       8. {
       9.   printf("Linux library test!\n");
    10. }

    # lt.h
       1. /* lt.h
       2. *
       3. */
       4.  
       5. void myprint(void);

    #test.c
       1. /* test.c
       2. *
       3. */
       4.  
       5. #include "lt.h"
       6.  
       7. int main(void)
       8. {
       9.   myprint();
    10.   return 0;
    11. }


    先看静态库
    首先做成静态库 liblt.a 。
       1. $ gcc -o lt.o -c lt.c
       2. $ ar cqs liblt.a lt.o

    再者,链接,
       1. $ gcc test.o liblt.a -o test

    这个时候再来看他的引用库情况。
       1. $ ldd test
       2.         linux-gate.so.1 => (0xffffe000)
       3.         libc.so.6 => /lib/libc.so.6 (0xb7e29000)
       4.         /lib/ld-linux.so.2 (0xb7f6e000)

    动态库
    做成动态库 liblt.so 。
       1. $ gcc -o lt.o -c lt.c
       2. $ gcc -shared -Wall -fPIC -o liblt.so lt.o  

    -shared 该选项指定生成动态连接库(让连接器生成T类型的导出符号表,有时候也生成弱连接W类型的导出符号),不用该标志外部程序无法连接。相当于一个可执行文件
    -fPIC:表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。

    -L.:表示要连接的库在当前目录中
    -ltest:编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.so来确定库的名称
    LD_LIBRARY_PATH:这个环境变量指示动态连接器可以装载动态库的路径。

    链接方法I,拷贝到系统库里再链接,让gcc自己查找

       1. $ sudo cp liblt.so /usr/lib
       2. $ gcc -o test test.o -llt
    这里我们可以看到了 -llt 选项,-l[lib_name] 指定库名,他会主动搜索
    lib[lib_name].so。这个搜索的路径可以通过 gcc --print-search-dirs来查找。

    链接方法II,手动指定库路径

       1. $ cc -o test test.o -llt -B /path/to/lib
    这里的-B 选项就添加 /path/to/lib 到gcc搜索的路径之中。这样链接没有问题但是方法II中手动链接好的程序在执行时候仍旧需要指定库路径(链接和执行是分开的)。需要添加系
    统变量 LD_LIBRARY_PATH :

       1. $ export LD_LIBRARY_PATH=/path/to/lib

    这个时候再来检测一下test程序的库链接状况(方法I情况)
       1. $ ldd test
       2.         linux-gate.so.1 => (0xffffe000)
       3.         liblt.so => /usr/lib/liblt.so (0xb7f58000)
       4.         libc.so.6 => /lib/libc.so.6 (0xb7e28000)
       5.         /lib/ld-linux.so.2 (0xb7f6f000)

    恩,是不是比静态链接的程序多了一个 liblt.so ?恩,这就是静态与动态的最大区别,静态情况下,他把库直接加载到程序里,而在动态链接的时候,他只是保留接口,将动态库与程序代码独立。这样就可以提高代码的可复用度,和降低程序的耦合度。
    另外,运行时,要保证主程序能找到动态库,所以动态库一般发布到系统目录中,要么就在跟主程序相对很固定的路径里,这样不管主程序在本机何时何地跑,都能找得到动态库。而静态库只作用于链接时,运行主程序时,静态库文件没存在意义了。


    ------------------------------------------------
    Linux中有两类函数库,分别是静态库和动态库。

    静态函数库:

    这类库的名字一般是libxxx.a;利用静态函数库编译成的文件比较大,因为整个函数库的所有数据都会被整合进目标代码中,他的优点就显而易见了, 即编译后的执行程序不需要外部的函数库支持,因为所有使用的函数都已经被编译进去了。当然这也会成为他的缺点,因为如果静态函数库改变了,那么你的程序必 须重新编译。

    动态函数库:

    这类库的名字一般是libxxx.so;相对于静态函数库,动态函数库在编译的时候并没有被编译进目标代码中,你的程序执行到相关函数时才调用该函数 库里的相应函数,因此动态函数库所产生的可执行文件比较小。由于函数库没有被整合进你的程序,而是程序运行时动态的申请并调用,所以程序的运行环境中必须 提供相应的库。动态函数库的改变并不影响你的程序,所以动态函数库的升级比较方便。

    linux系统有几个重要的目录存放相应的函数库,如/lib /usr/lib。

    下面来介绍linux静态函数库的创建和使用:

    例程str_out.h str_out.c main.c:

    str_out.h

    #ifndef STR_OUT_H

    #define STR_OUT_H

    void str_out(const char* str);

    #endif

    str_out.c

    #include

    #include "str_out.h"

    void str_out(const char* str){

    printf("%s\n",str);

    }

    main.c

    int main()

    {

    str_out("hello world");

    return 0;

    }

    不管是静态函数库还是动态函数库,都是由*.o目标文件生成。

    所以先gcc -c str_out.c

    静态函数库由ar命令创建

    本例:ar -cr libstr_out.a str_out.o

    -c create的意思

    -r replace的意思,表示当插入的模块名已经在库中存在,则替换同名的模块。如果若干模块中有一个模块在库中不存在,ar显示一个错误消息,并不替换其他同名模块。默认的情况下,新的成员增加在库的结尾处,可以使用其他任选项来改变增加的位置。

    到此静态函数库创建完毕。

    使用方法:通过gcc -o out main.c -L. -lstr_out编译main.c就会把静态函数库整合进out。

    其中

    -L指定静态函数库的位置供查找,注意L后面还有'.',表示静态函数库在本目录下查找。

    -l则指定了静态函数库名,由于静态函数库的命名方式是lib***.a,其中的lib和.a忽略。

    根据静态函数库的特性,此处删除libstr_out.a后out依然可以运行,因为静态库的内容已经整合进去了。

    动态函数库的创建和使用

    gcc -shared -fPCI -o out main.c -L. -lstr_out

    用该命令生成libstr_out.so 动态函数库。

    gcc -o out main.c

    此时还不能立即./out,因为在动态函数库使用时,会查找/usr/lib /lib目录下的动态函数库,而此时我们生成的库不在里边。

    这个时候有好几种方法可以让他成功运行:

    最直接最简单的方法就是把libstr_out.so拉到/usr/lib 或/lib中去。

    还有一种方法 export LD_LIBRARY_PATH=$(pwd)

    另外还可以在/etc/ld.so.conf文件里加入我们生成的库的目录,然后/sbin/ldconfig。

    /etc/ld.so.conf是非常重要的一个目录,里面存放的是链接器和加载器搜索共享库时要检查的目录,默认是从/usr/lib /lib中读取的,所以想要顺利运行,我们也可以把我们库的目录加入到这个文件中并执行/sbin/ldconfig

    另外还有个文件需要了解/etc/ld.so.cache,里面保存了常用的动态函数库,且会先把他们加载到内存中,因为内存的访问速度远远大于硬盘的访问速度,这样可以提高软件加载动态函数库的速度了。

    ***************************windows和linux下动态库的区别****************************************************************

    摘要:动态链接库技术实现和设计程序常用的技术,在Windows和Linux系统中都有动态库的概念,采用动态库可以有效的减少程序大小,节省空间,提高效率,增加程序的可扩展性,便于模块化管理。但不同操作系统的动态库由于格式不同,在需要不同操作系统调用时需要进行动态库程序移植。本文分析和比较了两种操作系统动态库技术,并给出了将Visual C++编制的动态库移植到Linux上的方法和经验。

    关键词:动态链接库 Linux编程 程序移植

    1 引言

    动 态库(Dynamic Link Library abbr,DLL)技术是程序设计中经常采用的技术。其目的减少程序的大小,节省空间,提高效率,具有很高的灵活性。采用动态库技术对于升级软件版本更加 容易。与静态库(Static Link Library)不同,动态库里面的函数不是执行程序本身的一部分,而是根据执行需要按需载入,其执行代码可以同时在多个程序中共享。

    在 Windows和Linux操作系统中,都可采用这种方式进行软件设计,但他们的调用方式以及程序编制方式不尽相同。本文首先分析了在这两种操作系统中通 常采用的动态库调用方法以及程序编制方式,然后分析比较了这两种方式的不同之处,最后根据实际移植程序经验,介绍了将VC++编制的Windows动态库 移植到Linux下的方法。

    2 动态库技术

    2.1 Windows动态库技术

    动 态链接库是实现Windows应用程序共享资源、节省内存空间、提高使用效率的一个重要技术手段。常见的动态库包含外部函数和资源,也有一些动态库只包含 资源,如Windows字体资源文件,称之为资源动态链接库。通常动态库以.dll,.drv、.fon等作为后缀。相应的windows静态库通常 以.lib结尾,Windows自己就将一些主要的系统功能以动态库模块的形式实现。

    Windows 动态库在运行时被系统加载到进程的虚拟空间中,使用从调用进程的虚拟地址空间分配的内存,成为调用进程的一部分。DLL也只能被该进程的线程所访问。 DLL的句柄可以被调用进程使用;调用进程的句柄可以被DLL使用。DLL模块中包含各种导出函数,用于向外界提供服务。DLL可以有自己的数据段,但没 有自己的堆栈,使用与调用它的应用程序相同的堆栈模式;一个DLL在内存中只有一个实例;DLL实现了代码封装性;DLL的编制与具体的编程语言及编译器 无关,可以通过DLL来实现混合语言编程。DLL函数中的代码所创建的任何对象(包括变量)都归调用它的线程或进程所有。

    根据调用方式的不同,对动态库的调用可分为静态调用方式和动态调用方式。

    (1) 静态调用,也称为隐式调用,由编译系统完成对DLL的加载和应用程序结束时DLL卸载的编码(Windows系统负责对DLL调用次数的计数),调用方式 简单,能够满足通常的要求。通常采用的调用方式是把产生动态连接库时产生的.LIB文件加入到应用程序的工程中,想使用DLL中的函数时,只须在源文件中 声明一下。 LIB文件包含了每一个DLL导出函数的符号名和可选择的标识号以及DLL文件名,不含有实际的代码。Lib文件包含的信息进入到生成的应用程序中,被调 用的DLL文件会在应用程序加载时同时加载在到内存中。

    (2)动态调用,即显式调用方式,是由编程者用API函数加载和卸载DLL来达到调用DLL的目的,比较复杂,但能更加有效地使用内存,是编制大型应用程序时的重要方式。在Windows系统中,与动态库调用有关的函数包括:

    ①LoadLibrary(或MFC 的AfxLoadLibrary),装载动态库。

    ②GetProcAddress,获取要引入的函数,将符号名或标识号转换为DLL内部地址。

    ③FreeLibrary(或MFC的AfxFreeLibrary),释放动态链接库。

    在 windows中创建动态库也非常方便和简单。在Visual C++中,可以创建不用MFC而直接用C语言写的DLL程序,也可以创建基于MFC类库的DLL程序。每一个DLL必须有一个入口点,在VC++ 中,DllMain是一个缺省的入口函数。DllMain负责初始化(Initialization)和结束(Termination)工作。动态库输出 函数也有两种约定,分别是基于调用约定和名字修饰约定。DLL程序定义的函数分为内部函数和导出函数,动态库导出的函数供其它程序模块调用。通常可以有下 面几种方法导出函数:

    ①采用模块定义文件的EXPORT部分指定要输入的函数或者变量。

    ②使用MFC提供的修饰符号_declspec(dllexport)。

    ③以命令行方式,采用/EXPORT命令行输出有关函数。

    在windows动态库中,有时需要编写模块定义文件(.DEF),它是用于描述DLL属性的模块语句组成的文本文件。

    2 Linux共享对象技术

    在Linux操作系统中,采用了很多共享对象技术(Shared Object),虽然它和Windows里 的动态库相对应,但它并不称为动态库。相应的共享对象文件以.so作为后缀,为了方便,在本文中,对该概念不进行专门区分。Linux系统的/lib以及 标准图形界面的/usr/X11R6/lib等目录里面,就有许多以so结尾的共享对象。同样,在Linux下,也有静态函数库这种调用方式,相应的后缀 以.a结束。Linux采用该共享对象技术以方便程序间共享,节省程序占有空间,增加程序的可扩展性和灵活性。Linux还可以通过LD-PRELOAD 变量让开发人员可以使用自己的程序库中的模块来替换系统模块。

    同Windows系统 一样,在Linux中创建和使用动态库是比较容易的事情,在编译函数库源程序时加上-shared选项即可,这样所生成的执行程序就是动态链接库。通常这 样的程序以so为后缀,在Linux动态库程序设计过程中,通常流程是编写用户的接口文件,通常是.h文件,编写实际的函数文件,以.c或.cpp为后 缀,再编写makefile文件。对于较小的动态库程序可以不用如此,但这样设计使程序更加合理。

    编 译生成动态连接库后,进而可以在程序中进行调用。在Linux中,可以采用多种调用方式,同Windows的系统目录(..\system32等)一样, 可以将动态库文件拷贝到/lib目录或者在/lib目录里面建立符号连接,以便所有用户使用。下面介绍Linux调用动态库经常使用的函数,但在使用动态 库时,源程序必须包含dlfcn.h头文件,该文件定义调用动态链接库的函数的原型。

    (1)_打开动态链接库:dlopen,函数原型void *dlopen (const char *filename, int flag);

    dlopen用于打开指定名字(filename)的动态链接库,并返回操作句柄。

    (2)取函数执行地址:dlsym,函数原型为: void *dlsym(void *handle, char *symbol);

    dlsym根据动态链接库操作句柄(handle)与符号(symbol),返回符号对应的函数的执行代码地址。

    (3)关闭动态链接库:dlclose ,函数原型为: int dlclose (void *handle);

    dlclose 用于关闭指定句柄的动态链接库,只有当此动态链接库的使用计数为0时,才会真正被系统卸载。 (4)动态库错误函数:dlerror,函数原型为: const char *dlerror(void); 当动态链接库操作函数执行失败时,dlerror可以返回出错信息,返回值为NULL时表示操作函数执行成功

    在取到函数执行地址后,就可以在动态库的使用程序里面根据动态库提供的函数接口声明调用动态库里面的函数。在编写调用动态库的程序的makefile文件时,需要加入编译选项-rdynamic和-ldl。

    除 了采用这种方式编写和调用动态库之外,Linux操作系统也提供了一种更为方便的动态库调用方式,也方便了其它程序调用,这种方式与Windows系统的 隐式链接类似。其动态库命名方式为“lib*.so.*”。在这个命名方式中,第一个*表示动态链接库的库名,第二个*通常表示该动态库的版本号,也可以 没有版本号。在这种调用方式中,需要维护动态链接库的配置文件/etc/ld.so.conf来让动态链接库为系统所使用,通常将动态链接库所在目录名追 加到动态链接库配置文件中。如具有X window窗口系统发行版该文件中都具有/usr/X11R6/lib,它指向X window窗口系统的动态链接库所在目录。为了使动态链接库能为系统所共享,还需运行动态链接库的管理命令./sbin/ldconfig。在编译所引用的动态库时,可以在gcc采用 –l或-L选项或直接引用所需的动态链接库方式进行编译。在Linux里面,可以采用ldd命令来检查程序依赖共享库。

    3 两种系统动态库比较分析

    Windows和Linux采用动态链接库技术目的是基本一致的,但由于操作系统的不同,他们在许多方面还是不尽相同,下面从以下几个方面进行阐述。

    (1) 动态库程序编写,在Windows系统下的执行文件格式是PE格式,动态库需要一个DllMain函数作为初始化的人口,通常在导出函数的声明时需要有 _declspec(dllexport)关键字。Linux下的gcc编译的执行文件默认是ELF格式,不需要初始化入口,亦不需要到函数做特别声明, 编写比较方便。

    (2)动态库编译,在windows系统下面,有方便的调试编译环境,通常不用自己去编写makefile文件,但在linux下面,需要自己动手去编写makefile文件,因此,必须掌握一定的makefile编写技巧,另外,通常Linux编译规则相对严格。

    (3)动态库调用方面,Windows和Linux对其下编制的动态库都可以采用显式调用或隐式调用,但具体的调用方式也不尽相同。

    (4) 动态库输出函数查看,在Windows中,有许多工具和软件可以进行查看DLL中所输出的函数,例如命令行方式的dumpbin以及VC++工具中的 DEPENDS程序。在Linux系统中通常采用nm来查看输出函数,也可以使用ldd查看程序隐式链接的共享对象文件。

    (5)对操作系统的依赖,这两种动态库运行依赖于各自的操作系统,不能跨平台使用。因此,对于实现相同功能的动态库,必须为两种不同的操作系统提供不同的动态库版本。



    另一种解释:


      我们在编写一个C语言程序的时候,经常会遇到好多重复或常用的部分,如果每次都重新编写固然是可以的,不过那样会大大降低工作效率,并且影响代码的可读性,更不利于后期的代码维护。我们可以把他们制作成相应的功能函数,使用时直接调用就会很方便,还可以进行后期的功能升级。 

     

         例如我要在一段代码中多次交换两个变量的值,我可以在代码中多次写入

    i=x;
    x=y;
    y=i;

           不过这样未免有点麻烦我们可以编写一个change_two_int()函数进行简化。
    定义如下函数:
    void change_two_int(int *a,int *b)
      {
         int c;
         c=*a;
         *a=*b;
         *b=c;
       }
        这样每次要进行交换时只需调用 change_two_int(&x , &y);即可,是否方便了许多?

           那么我们要讨论的和这些有什么关系呢?库通俗的说就是把这些常用函数的目标文件打包在一起,提供相应函数的接口,便于程序员使用。库是别人写好的现有的,成熟的,可以复用的代码,我们只需要知道其接口如何定义,便可以自如使用。

           现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常。比如我们常使用的printf函数,就是c标准库提供的函数。我们在使用时只需要包含相应的头文件就可以使用(非静态编译还要有相应的库文件)。而不用关心printf函数具体是如何实现的,这样就大大提高了程序员编写代码的效率。从使用方法上分库大体上可以分为两类:静态库和共享库。在windows中静态库是以 .lib 为后缀的文件,共享库是以 .dll 为后缀的文件。在linux中静态库是以 .a 为后缀的文件,共享库是以 .so为后缀的文件。
    以linux下的静态库和动态库为例我们研究一下,首先我们看一下他们的生成方式

    静态库:
    首先将源文件编译成目标文件:gcc –c a.c b.c
    生成静态库:ar –rc libstatic.a a.o b.o

    共享库:
    同静态库一样编译成目标文件:gcc –c a.c b.c
    生成共享库:gcc –fPIC –shared –o libshared.so a.o b.o

           由此可见静态库和动态库都是对目标文件的处理,也可以说库文件已经是机器码文件了,静态库和共享库的加载过程有很大的区别。

    静态库的链接方法:
    gcc –o staticcode –L. –lstatic main.c –static(默认库在当前文件夹)

    共享库的链接方法: 
    gcc –o sharedcode  -L. –lshared main.c(默认库在当前文件夹)

           当程序与静态库连接时,库中目标文件所含的所有将被程序使用的函数的机器码被copy到最终的可执行文件中。这就会导致最终生成的可执行代码量相对变多,相当于编译器将代码补充完整了,这样运行起来相对就快些。不过会有个缺点: 占用磁盘和内存空间. 静态库会被添加到和它连接的每个程序中, 而且这些程序运行时, 都会被加载到内存中. 无形中又多消耗了更多的内存空间.

           与共享库连接的可执行文件只包含它需要的函数的引用表,而不是所有的函数代码,只有在程序执行时, 那些需要的函数代码才被拷贝到内存中。这样就使可执行文件比较小, 节省磁盘空间,更进一步,操作系统使用虚拟内存,使得一份共享库驻留在内存中被多个程序使用,也同时节约了内存。不过由于运行时要去链接库会花费一定的时间,执行速度相对会慢一些,总的来说静态库是牺牲了空间效率,换取了时间效率,共享库是牺牲了时间效率换取了空间效率,没有好与坏的区别,只看具体需要了。

            另外,.一个程序编好后,有时需要做一些修改和优化,如果我们要修改的刚好是库函数的话,在接口不变的前提下,使用共享库的程序只需要将共享库重新编译就可以了,而使用静态库的程序则需要将静态库重新编译好后,将程序再重新编译一便。

     

    总结:

     

    一、库的类型

    (一) 在windows中

    .dll 动态库

    .lib 静态库

    库即为源代码的二进制文件

    (二) 在linux中

    .so 动态库

    .a      静态库

     

    (三) 静态库和动态库的优缺点

    我们通常把一些公用函数制作成函数库,供其它程序使用。

    函数库分为静态库和动态库两种。

    静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库。

    动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入,因此在程序运行时还需要动态库存在

     

    1.什么是库

    在windows平台和linux平台下都大量存在着库。

    本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。

    由于windows和linux的本质不同,因此二者库的二进制是不兼容的。

    本文仅限于介绍linux下的库

     

    2.库的种类

    linux下的库有两种:静态库和共享库(动态库)。

     二者的不同点在于代码被载入的时刻不同。

    静态库的代码在编译过程中已经被载入可执行程序,因此体积较大。

    共享库的代码是在可执行程序运行时才载入内存的,在编译过程中仅简单的引用,因此代码体积较小。

     

    3.库存在的意义

    库是别人写好的现有的,成熟的,可以复用的代码,你可以使用但要记得遵守许可协议。

    现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常。共享库的好处是,不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例。

     

    4.库文件是如何产生的在linux下

    静态库的后缀是.a,它的产生分两步

    Step 1.由源文件编译生成一堆.o,每个.o里都包含这个编译单元的符号表

    Step 2.ar命令将很多.o转换成.a,成文静态库

    动态库的后缀是.so,它由gcc加特定参数编译产生。

    例如:

    $ gcc -fPIC -c *.c $ gcc -shared -Wl,-soname, libfoo.so.1 -olibfoo.so.1.0 *.

     

    5.库文件是如何命名的,有没有什么规范

    在linux下,库文件一般放在/usr/lib和/lib下,

    静态库的名字一般为libxxxx.a,其中xxxx是该lib的名称

    动态库的名字一般为libxxxx.so.major.minor,xxxx是该lib的名称,major是主版本号, minor是副版本号

     

    6.如何知道一个可执行程序依赖哪些库

    ldd命令可以查看一个可执行程序依赖的共享库,

    例如# ldd /bin/lnlibc.so.6

    => /lib/libc.so.6 (0×40021000)/lib/ld-linux.so.2

    => /lib/ld- linux.so.2 (0×40000000)

    可以看到ln命令依赖于libc库和ld-linux库

     

    7.可执行程序在执行的时候如何定位共享库文件

    当系统加载可执行代码时候,能够知道其所依赖的库的名字,但是还需要知道绝对路径

    此时就需要系统动态载入器(dynamiclinker/loader)

    对于elf格式的可执行程序,是由ld-linux.so*来完成的

    它先后搜索elf文件的 DT_RPATH段—环境变量LD_LIBRARY_PATH—/etc/ld.so.cache文件列表—/lib/,/usr/lib目录

    找到库文件后将其载入内存

     

    8.在新安装一个库之后如何让系统能够找到他

    如果安装在/lib或者/usr/lib下,那么ld默认能够找到,无需其他操作。

    如果安装在其他目录,需要将其添加到/etc/ld.so.cache文件中,步骤如下

    1.编辑/etc/ld.so.conf文件,加入库文件所在目录的路径

    2.运行ldconfig,该命令会重建/etc/ld.so.cache文件

     

    ############################################################

     

     linux中编译静态库(.a)和动态库(.so)的基本方法

     

    (四) 静态库

     

    在linux环境中, 使用ar命令创建静态库文件.如下是命令的选项:

              d -----从指定的静态库文件中删除文件

              m -----把文件移动到指定的静态库文件中

              p -----把静态库文件中指定的文件输出到标准输出

              q -----快速地把文件追加到静态库文件中

              r -----把文件插入到静态库文件中

              t -----显示静态库文件中文件的列表

              x -----从静态库文件中提取文件

          还有多个修饰符修改以上基本选项,详细请man ar 以下列出三个:

              a -----把新的目标文件(*.o)添加到静态库文件中现有文件之后

              b-----***************************************之前

              v -----使用详细模式

    ar 命令的命令行格式如下:

          ar[-]{dmpqrtx}[abcfilNoPsSuvV] [membername] [count] archive files...

    参数archive定义库的名称, files是库文件中包含的目标文件的清单, 用空格分隔每个文件.

     

    比如创建一个静态库文件的命令如下:

          ar r libapue.a error.oerrorlog.o lockreg.o

    这样就了libapue.a静态库文件, 可以用 t 选项显示包含在库中的文件

    创建库文件之后,可以创建这个静态库文件的索引来帮助提高和库连接的其他程序的编译速度:

    使用ranlib程序创建库的索引,索引存放在库文件内部.

          ranlib libapue.a

     

    用nm程序显示存档文件的索引,它可以显示目标文件的符号

    nm libapue.a | more

    如果是显示目标文件的符号:

    nm error.o | more

    如何使用呢?如下所示:

    gcc -o test test.c libapue.a

    这样就可以在test.c中调用在libapue.a中的函数了.

     

     

    (五) 动态库

     

    1.创建共享库

         gcc -shared -o libapue.soerror.o errorlog.o

    这样就创建了共享库!

    2.编译共享库

        假设共享库位于当前目录(即跟程序文件相同的目录中)

    gcc -o test -L. -lapue test.c

    这样就编译出了不包含函数代码可执行文件了,但是但你运行时会发现linux动态加载器找不到libapue.so文件.

    可以用ldd 命令查看可执行文件依赖什么共享库:

    ldd test

    如何才能让动态加载器发现库文件呢?有两种方法可以解决:

        1.环境变量

           exportLD_LIBRARY_PATH="$LD_LIBRARY_PATH:."

        2.修改/etc/ld.so.conf文件.

     

    一般应用程序的库文件不与系统库文件放在同一个目录下,一般把应用程序的共享库文件放在/usr/local/lib下,新建一个属于自己的目录apue,然后把刚才libapue.so复制过去就行了

    同时在/etc/ld.so.conf中新增一行:

    /usr/local/lib/apue

     

    以后在编译程序时加上编译选项:

    -L /usr/local/lib/apue -lapue

     

    /*

    参数的配置通过mangcc可以看到

    -llibrary

                  连接名为 library 的 库文件.

                  连接器 在 标准搜索目录 中 寻找 这个 库文件, 库文件 的 真正 名 字


    展开全文
  • 动态库和静态库区别

    千次阅读 2018-09-30 23:38:46
    1. 静态库:函数数据被编译进一个二进制文件(通常扩展名为....2. 在使用动态库的时候,一般提供两个文件:一个引入库(.lib)一个DLL。 引入库(.lib)包含被DLL导出的函数变量的符号名 DLL包含实际的...

    1. 静态库:函数和数据被编译进一个二进制文件(通常扩展名为.LIB)。

    • 在编译链接可执行文件时,链接器(Linker)从库中复制这些函数和数据并把它们和应用程序的其它模块组合起来创建最终的可执行文件(.EXE文件)。

    2. 在使用动态库的时候,一般提供两个文件:一个引入库(.lib)和一个DLL。

    • 引入库(.lib)包含被DLL导出的函数和变量的符号名

    • DLL包含实际的函数和数据。

    • 在编译链接可执行文件(.EXE文件)时,只需要链接引入库,DLL中的函数代码和数据并不复制到可执行文件中

    • 在运行可执行文件(.EXE文件)的时候,再去加载DLL,访问DLL中导出的函数。

    更多内容请看C/C++动态链接库(DLL)详解

    来源:孙鑫_VC++_学习笔记

     

    展开全文
  • iOS中的动态库和静态库的区别

    千次阅读 2018-11-29 15:25:00
    2019独角兽企业重金招聘Python工程师标准>>> ...
  • 什么是动态链接库和静态链接库?它们有什么区别?它们分别如何使用?
  • 动态库和静态库的区别及使用

    千次阅读 2011-12-06 11:25:40
    静态库和动态库的区别 1. 静态函数库  这类库的名字一般是libxxx.a;利用静态函数库编译成的文件比较大,因为整个 函数库的所有数据都会被整合进目标代码中,他的优点就显而易见了,即编译后的执行程序不...
  • 动态链接库和静态链接库的区别

    万次阅读 2018-05-29 15:03:43
    两种:一种是LIB包含了函数所在DLL文件文件中函数位置信息(入口),代码由运行时加载在进程空间中DLL提供,称为动态链接dynamic link library。一种是LIB包含函数代码本身,在编译时直接将代码加入程序...
  • 函数库分为静态库和动态库两种。 静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库。 Android.mk 编译成静态库:include $(BUILD_SHARED_LIBRARY) 使用该静态库:LOCAL_SHARED_LIBRARY += 生成...
  • linux中静态库和动态库的区别和编译

    千次阅读 2014-02-26 16:45:57
    linux中静态库和动态库的区别和编译  Linux库有动态与静态两种,动态通常用.so为后缀,静态用.a为后缀。例如:libhello.so libhello.a  为了在同一系统中使用不同版本的库,可以在库文件名后加上版本号为...
  • linux windows静态库和动态库的区别

    千次阅读 2012-01-17 16:45:04
    注意:Linux 静态编译时将动态库也编入文件中。 文件预览 文件目录树如下,如你所见,非常简单。  1. libtest/  2. |-- lt.c  3. |-- lt.h  4. `-- test.c 代码 #lt.c  1. /* lt.c  2. * ...
  • 在使用静态库的情况下,在编译连接可执行文件时,连接器从库中复制这些函数数据并把他们应用程序的其他模块组合起来创建最终的可执行文件(.exe)文件,当发布产品时,只需要发布这个可执行文件,并不需要发布被...
  • 静态链接库和动态链接库的区别

    千次阅读 2018-04-23 20:45:30
    在VS2015平台上,创建一个静态库(.lib)项目方案,选择【创建项目/Win32/Win32控制台应用程序/静态库(.lib)】,如下图所示, 生成项目后,创建两个项目文件staticdll.cppstaticdll.h,并添加如下代码, //stat...
  • 静态库和动态库的区别(转)

    千次阅读 2010-01-12 17:29:00
    libdll文件的区别和联系 .dll是在你的程序运行的时候才连接的文件,因此它是一种比较小的可执行文件格式,.dll还有其他的文件格式如.ocx等,所有的.dll文件都是可执行。 .lib是在你的程序编译连接的时候就连接的...
  • 静态连接库和动态链接库的区别

    千次阅读 2009-10-10 15:31:00
    一般我们称之为静态库和动态库(DLL)。 什么是静态连接库(static link library),什么是动态链接库(dynamic link library) 首先,静态链接库与动态链接库都是共享代码方式。 如果采用静态链接库
  • 静态库和动态库的区别(面试题)

    千次阅读 2019-08-01 14:59:32
    目前以lib为后缀的库有两种,一种为静态链接库,另一种则为动态连接库的导入库。 则他们的区别如下: 静态库 静态函数库的扩展名一般为(.a或.lib),这类的函数库通常扩展名为libxxx.a或xxx.lib 。 这类库在编译的...
  • 创建MFC项目时,选择动态库和静态库的区别动态库在VS2017版本中,选择的时候是叫“在共享DLL中使用MFC”。这样创建的时候生成的*.exe文件一般较小,原因有几点1. 此*.exe程序内部,一般不会包含文件所需要的库,其...
  • 静态库相当于复制一份库文件到项目代码中,不需要像动态库那样需要有动态加载,识别依赖函数地址开销。 同样,静态连接库文件比动态链接库文件需要更少内存去搜寻函数在动加载或共享库中地址。 缺点: ...
  • 关于c/c++静态库和动态库的区别

    千次阅读 2014-05-28 15:09:22
    关于c/c++静态库和动态库的区别: 1 每个源文件(.c .cpp等)首先被编译为汇编,然后然后被编译为.o(或其他格式)的目标文件。如果是本文件已经实现了的函数,那么这个函数有个地址。没有实现的或者引用的别人的...
  • 1 静态库 在共享库出现之前,公用功能是以静态库的形式存在的,它把通用功能模块的多个目标文件打包在一起,用到它的程序只需要在链接时指定这个库文件,链接器就会从这个库中抽取出用到的功能代码拷贝到目标程序中...
  • 静态连接就是把(lib)文件中用到函数代码直接链接进目标程序,程序运行时候... 静态链接动态链接都是共享代码方式,如果采用静态链接,则无论你愿不愿意,lib 中指令都全部被直接包含在最终生成 EX
  • 一、静态库和动态库1、静态库(.lib) 函数和数据被编译进一个二进制文件(通常扩展名为.LIB)。在使用静态库的情况下,在编译链接可执行文件时,链接器从库中复制这些函数和数据并把它们和应用程序的其他模块组合...
  • 静态库动态库的区别

    千次阅读 2018-07-03 21:27:28
    什么是库库是写好现有,...库有两种:静态库(.a、.lib)和动态库(.so、.dll)。所谓静态、动态是指链接。回顾一下,将一个程序编译成可执行程序步骤:静态库之所以成为【静态库】,是因为在链接阶段,会将汇...
  • 无聊,遂准备写一篇博客,介绍一下CC++运行,只因发现工作几年人对此一知半解大有人在。    在使用VC构建项目时,经常会遇到下面链接错误:      初学者面对这些错误常常不知所错:...

空空如也

1 2 3 4 5 ... 20
收藏数 80,367
精华内容 32,146
关键字:

动态库