iOS:动态库

作者: MonKey_Money | 来源:发表于2021-01-25 15:16 被阅读0次

    1.动态库原理

    1.1自己生成动态库(失败)

    我们还是用在静态库中的TestExample案例先生成动态库

    image.png
    为了方便我们使用创建build.sh,来使用脚本
    clang -target x86_64-apple-macos11.0.1 \
    -fobjc-arc \
    -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
    -I./dylib \
    -c test.m -o test.o
    
    pushd ./dylib
    echo "编译TestExample.m --- TestExample.o"
    clang -target x86_64-apple-macos11.0.1 \
    -fobjc-arc \
    -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
    -c TestExample.m -o TestExample.o
    clang -dynamiclib \
    -target x86_64-apple-macos11.0.1 \
    -fobjc-arc \
    -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
    TestExample.o -o libTestExample.dylib
    echo "编译TestExample.m --- libTestExample.dylib"
    clang -target x86_64-apple-macos11.0.1 \
    -fobjc-arc \
    -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
    -L./dylib \
    -lTestExample \
    test.o -o test
    

    运行后报错还是和上面一样 dyld: Library not loaded: libTestExample.dylib

    1.2.先生成静态库再链接成动态库

    //把我们的o文件变成a文件。也可以使用ar -rc a.a a.o
    libtool -static -arch_only x86_64 TestExample.o -o libTestExample.a
    
    ld -dylib -arch x86_64 \
    -macosx_version_min 11.0 \
    -syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
    -lsystem -framework Foundation \
    -all_load \  //如果没有这个参数,链接器会默认not all load
    libTestExample.a -o libTestExample.dylib
    

    运行之后还是报错

    Process 25266 launched: '/Users/MacW/Desktop/loginlearn/强化版/强化班-4-动态库/上课代码/动态库原理/test' (x86_64)
    dyld: Library not loaded: libTestExample.dylib
      Referenced from: /Users/MacW/Desktop/loginlearn/强化版/强化班-4-动态库/上课代码/动态库原理/test
      Reason: image not found
    

    错误显示库没有加载进来
    我们查看test加载所需要的动态库

    otool -l test | grep 'DYLIB' -A 5
    //日志如下
     cmd LC_LOAD_DYLIB
          cmdsize 48
             name libTestExample.dylib (offset 24)
       time stamp 2 Thu Jan  1 08:00:02 1970
          current version 0.0.0
    compatibility version 0.0.0
    --
              cmd LC_LOAD_DYLIB
          cmdsize 96
             name /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (offset 24)
       time stamp 2 Thu Jan  1 08:00:02 1970
          current version 1770.106.0
    compatibility version 300.0.0
    --
              cmd LC_LOAD_DYLIB
          cmdsize 56
             name /usr/lib/libobjc.A.dylib (offset 24)
       time stamp 2 Thu Jan  1 08:00:02 1970
          current version 228.0.0
    compatibility version 1.0.0
    --
              cmd LC_LOAD_DYLIB
          cmdsize 56
             name /usr/lib/libSystem.B.dylib (offset 24)
       time stamp 2 Thu Jan  1 08:00:02 1970
          current version 1292.0.0
    compatibility version 1.0.0
    --
              cmd LC_LOAD_DYLIB
          cmdsize 104
             name /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (offset 24)
       time stamp 2 Thu Jan  1 08:00:02 1970
          current version 1770.106.0
    compatibility version 150.0.0
    

    我们看到我们自己创建的库 name libTestExample.dylib (offset 24),而其他的动态库的name确实库的路径。
    我们的动态库能够自己保存自己的路径

    otool -l ./dylib/libTestExample.dylib | grep 'ID' -A 5
    //-A是向下寻找5行,-B是向上查找
     cmd LC_ID_DYLIB
          cmdsize 48
             name libTestExample.dylib (offset 24)
       time stamp 1 Thu Jan  1 08:00:01 1970
          current version 0.0.0
    compatibility version 0.0.0
    --
         cmd LC_UUID
     cmdsize 24
        uuid F26CDB5B-F71B-3658-86BE-C93CBE279ABE
    Load command 9
           cmd LC_BUILD_VERSION
       cmdsize 32
    

    我们看到name 还是 libTestExample.dylib
    我们可以使用install_name_tool去给动态库添加路径

    man install_name_tool
    NAME
           install_name_tool - change dynamic shared library install names
    

    install_name_tool就是改变动态的路径。
    给动态路添加路径

    install_name_tool -id /Users/MacW/Desktop/loginlearn/强化版/强化班-4-动态库/上课代码/动态库原理/dylib/libTestExample.dylib  libTestExample.dylib
    

    重新查看动态库的LC_ID_DYLIB

    otool -l libTestExample.dylib | grep 'ID' -A 5
     cmd LC_ID_DYLIB
          cmdsize 144
             name /Users/MacW/Desktop/loginlearn/强化版/强化班-4-动态库/上课代码/动态库原理/dylib/libTestExample.dylib (offset 24)
       time stamp 1611538070 Mon Jan 25 09:27:50 2021
          current version 0.0.0
    compatibility version 0.0.0
    --
         cmd LC_UUID
     cmdsize 24
        uuid F26CDB5B-F71B-3658-86BE-C93CBE279ABE
    Load command 9
           cmd LC_BUILD_VERSION
       cmdsize 32
    

    然后重新编译和链接我们的test文件运行成功
    我们通过install_name_tool,重新给编译过的动态库加的路径,其实ld提供给我们一个参数可以直接加的install_name
    所以我们初期的sh脚本如下

    clang -target x86_64-apple-macos11.0.1 \
    -fobjc-arc \
    -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
    -I./dylib \
    -c test.m -o test.o
    
    pushd ./dylib
    echo "编译TestExample.m --- TestExample.o"
    clang -target x86_64-apple-macos11.0.1 \
    -fobjc-arc \
    -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
    -c TestExample.m -o TestExample.o
    
    echo "编译TestExample.o --- libTestExample.a"
    
    # Xcode->静态库
    libtool -static -arch_only x86_64 TestExample.o -o libTestExample.a
    # -dynamiclib: 动态库
    # dylib 最终链接产物 -》
    ld -dylib -arch x86_64 \
    -macosx_version_min 11.0 \
    -syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
    -lsystem -framework Foundation \
    -install_name /Users/MacW/Desktop/loginlearn/强化版/强化班-4-动态库/上课代码/动态库原理/dylib/libTestExample.dylib \
    -all_load \
    libTestExample.a -o libTestExample.dylib
    popd
    echo "链接libTestExample.dylib -- test EXEC"
    clang -target x86_64-apple-macos11.0.1 \
    -fobjc-arc \
    -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
    -L./dylib \
    -lTestExample \
    test.o -o test
    

    运行没问题

    2.动态库复用性

    我们在上面指定的路径是我们的绝对路径,如果动态库换了位置,很明显还是会出现之前出现的问题, Reason: image not found

    2.1@rpath解决绝对路径的困扰

    @rpath
    Runpath search Paths
    dyld搜索路径
    运行时@rpath指示dyld按顺序搜索路径列表,以找到动态库。
    @rpath保存一个或多个路径的变量
    所以我们修改脚本

    -install_name @rpath/dylib/libTestExample.dylib
    

    运行还是报错,为什么呢?我们可以查看test文件是否有rpath的设置

    2.2 rpath

    otool -l  test | grep 'RPATH' -A 5
    

    我们通过install_name_tool给test添加rpath路径

     install_name_tool -add_rpath /Users/MacW/Desktop/loginlearn/强化版/强 化班-4-动态库/上课代码/动态库原理 test
    

    正常运行。但是add_rpath也是我们手动添加的,没有可复用性。

    2.3.executable_path和loader_path

    @executable_path:表示可执行程序所在的目录,解析为可执行文件的绝对路径。
    @loader_path:表示被加载的Mach-O 所在的目录,每次加载时,都可能 被设置为不同的路径,由上层指定。

    install_name_tool -rpath /Users/MacW/Desktop/loginlearn/强化版/强化班-4-动态库/上课代码/动态库原理  @executable_path test
    

    运行test成功

    3.动态库链接动态库

    test.m文件使用Frameworks中的TestExample.framework
    在TestExample.framework库中使用TestExampleLog.framework

    3.1先编译并链接TestExampleLog.framework

    clang -target x86_64-apple-macos11.0.1 \
    -fobjc-arc \
    -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
    -I./Headers \
    -c TestExampleLog.m -o TestExampleLog.o
    
    clang -dynamiclib  \
    -target x86_64-apple-macos11.0.1 \
    -fobjc-arc \
    -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
    -Xlinker -install_name -Xlinker @rpath/TestExampleLog.framework/TestExampleLog \
    TestExampleLog.o -o TestExampleLog
    

    install_name是提供给外部需要链接TestExampleLog的路径。
    通过查看otool -l TestExampleLog | grep 'ID' -A 5能看出外部需要链接的路径

              cmd LC_ID_DYLIB
          cmdsize 72
             name @rpath/TestExampleLog.framework/TestExampleLog (offset 24)
       time stamp 1 Thu Jan  1 08:00:01 1970
          current version 0.0.0
    compatibility version 0.0.0
    --
         cmd LC_UUID
     cmdsize 24
        uuid 73F8F987-0378-346D-B014-890611B76871
    Load command 9
           cmd LC_BUILD_VERSION
       cmdsize 32
    

    3.2.再编译链接TestExample.framework

    clang -target x86_64-apple-macos11.0.1 \
    -fobjc-arc \
    -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
    -I./Headers \
    -I./Frameworks/TestExampleLog.framework/Headers \
    -c TestExample.m -o TestExample.o
    
    clang -dynamiclib  \
    -target x86_64-apple-macos11.0.1 \
    -fobjc-arc \
    -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
    -Xlinker -install_name -Xlinker @rpath/TestExample.framework/TestExample \
    -Xlinker -rpath -Xlinker @loader_path/Frameworks \
    -F./Frameworks \
    -framework TestExampleLog \
    TestExample.o -o TestExample
    
    
    echo "-------DYLIB---------"
    otool -l TestExample | grep 'DYLIB' -A 5
    echo "-------ID---------"
    otool -l TestExample | grep 'ID' -A 5
    

    install_name是提供给调用者加载的路径
    rpath是提供给调用TestExampleLog的初始路径

    3.3编译并链接test

    clang -target x86_64-apple-macos11.0.1 \
    -fobjc-arc \
    -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
    -I./Frameworks/TestExample.framework/Headers \
    -c test.m -o test.o
    
    clang   \
    -target x86_64-apple-macos11.0.1 \
    -fobjc-arc \
    -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
    -Xlinker -rpath -Xlinker @executable_path/Frameworks \
    -F./Frameworks \
    -framework TestExample \
    test.o -o test
    

    运行正常打印

    3.4.test文件怎么使用TestExampleLog

    如果我们想使用TestExampleLog,我们需要能再TestExample中导出符号中能看到TestExampleLog

    objdump --macho --exports-trie TestExample
    TestExample:
    Exports trie:
    0x000080C0  _OBJC_METACLASS_$_TestExample
    0x000080E8  _OBJC_CLASS_$_TestExample
    

    我们没有看出有导出符号,所以我们无法在test使用TestExampleLog,我们需要使用reexport_framework
    我们修改TestExample内的脚本

    clang -dynamiclib  \
    -target x86_64-apple-macos11.0.1 \
    -fobjc-arc \
    -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
    -Xlinker -install_name -Xlinker @rpath/TestExample.framework/TestExample \
    -Xlinker -rpath -Xlinker @loader_path/Frameworks \
    -Xlinker -reexport_framework -Xlinker TestExampleLog \
    -F./Frameworks \
    -framework TestExampleLog \
    TestExample.o -o TestExample
    

    查看DYLIIB信息

     otool -l TestExample | grep 'DYLIB' -A 5
      cmd LC_ID_DYLIB
          cmdsize 72
             name @rpath/TestExample.framework/TestExample (offset 24)
       time stamp 1 Thu Jan  1 08:00:01 1970
          current version 0.0.0
    compatibility version 0.0.0
    --
              cmd LC_REEXPORT_DYLIB
          cmdsize 72
             name @rpath/TestExampleLog.framework/TestExampleLog (offset 24)
       time stamp 2 Thu Jan  1 08:00:02 1970
          current version 0.0.0
    compatibility version 0.0.0
    

    可以知道TestExample重新加一个cmd,LC_REEXPORT_DYLIB
    我们在test中导入头文件

    #import <Foundation/Foundation.h>
    #import "TestExample.h"
    #import "TestExampleLog.h"
    
    int main(){
        NSLog(@"testApp----");
        TestExample *manager = [TestExample new];
        [manager lg_test: nil];
        TestExampleLog *log = [TestExampleLog new];
        NSLog(@"testApp----%@",log);
        return 0;
    }
    

    我们的脚本文件也需要-I头文件,重新运行脚本,正常打印。

    相关文章

      网友评论

        本文标题:iOS:动态库

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