美文网首页
iOS|Framework静态库打包教程 揭秘大牛和萌新的操作究

iOS|Framework静态库打包教程 揭秘大牛和萌新的操作究

作者: BinaryBang | 来源:发表于2020-06-09 22:12 被阅读0次

    本文主要讲述了如何从0打出一个名称为GaPersonFramework.Framework的静态库.
    从而让大家了解Framework静态库是如何制作的.

    其中,最关键的第5步,牛人和萌新有着各自不同的做法,想知道你是牛人水平,还是萌新水平么?
    那么,请耐着性子看完本文吧O(∩_∩)O

    1 明确要编译的指令集

    首先,我们需要明确自己要打的库文件需要支持哪些指令集,指令集列表以及对应的设备如下:
    armv7:
    iPhone 3GS, iPhone 4, iPhone 4S, iPod 3G/4G/5G, iPad, iPad 2, iPad 3, iPad Mini
    armv7s:
    iPhone 5, iPhone 5c, iPad 4
    arm64:
    iPhone X,iPhone 8(Plus),iPhone 7(Plus),iPhone 6(Plus),iPhone 6s(Plus), iPhone 5s, iPad Air(2), Retina iPad Mini(2,3)
    arm64e:
    iPhone XS\XR\XS Max
    i386:
    32位设备的模拟器
    x86_64:
    64位设备的模拟器

    一般情况下,我们只要编译得到armv7,arm64,i386,x86_64指令集的库就可以了.
    arm64e可以使用arm64的库文件运行,armv7s 可以使用armv7的库文件运行.
    虽然性能不能充分发挥,但是控制了库文件的大小.当然,如果要充分发挥设备性能,最好还是为arm64e和armv7s也构造库文件.

    但是这里我们还是将目标定位arvmv7,arm64,i386,x86_64这四个体系.

    2 创建Framework工程

    下面,我们建立一个Framework工程,此类工程最终会输出一个Framework.
    工程名字为GaPersonFramework.


    系统默认为我们创建了一个GaPersonFramework.h文件


    3 编写库文件

    接下来,我们编写库文件.
    在GaPersonFramework中会创建两个文件.
    一个是GaPerson类,表示人类,该类有一个greet方法,简单地输出字符串"Nice to meet you,I am kind of GaPerson".
    另一个是NSObject+GaPerson分类,分类中也定义了一个greet方法,输出字符串"Nice to meet you,I am kind of NSObject.".
    代码比较简单,就不详细介绍了.


    4 设置工程

    4.1 设置库文件类型

    选中Targets-GaPersonFramework->BuildSettings->Linking->Mach-O Type
    设置为 Static Library.这个是静态库.
    该选项默认是Dynamic Library,不是我们所需要的.


    4.2 设置架构体系

    在BuildSettings->Architectures选项栏中,有3个值需要选择:
    **1,Architectures,设置为(ARCHS_STANDARD)** 在当前版本的Xcode中,(ARCHS_STANDARD)等价于arm64,armv7两个.
    2,Build Active Architecture Only,在Release模式是设置为NO,Debug模式设置为YES.
    是否只编译正在运行的设备对应的指令集的库文件.
    Debug模式设置为YES,是为了加快调式速度.
    Release模式设置为NO,是为了得到全部所需的指令集的库文件.
    3,Valid Architectures,设置为arm64,armv7,i386和x86_64
    当BuildActiveArchitectureOnlu为NO时,Xcode最终将编译得到ValidArchitecture和Architectures这两个选项的交集的指令集的程序.

    4.3 添加公共头文件

    1,在BuildPhases->Headers中,把要公开的头文件从Project区域转移到Public区域
    在这里加入到Public区域的头文件,做成Framework库文件的时候才会暴露出来.

    2,在GaPersonFramework.h中添加公开头文件

    到此为止,需要的配置已经Ok,接下来构建库文件.

    5 构建库文件

    5.1 萌新之手动构建

    1,编译arm64和armv7的版本

    选择设备为Generic iOS Device,然后Scheme的模式选择为Release,然后cmd+B.


    编译成功,在Products文件夹下就会有一个GaPersonFramework.framework出现.
    注意这个库文件,只包含真机设备的指令集,并不是我们最终要的.


    我们选中该文件,右键点击ShowInFinder
    可以看到这个包在Release-iphoneos文件夹下,而子目录中的GaPersonFramework,就是我们的库文件.


    使用以下命令查看一个库文件支持的指令集:
    lipo -info LibPath

    注意,只要把文件拖动到终端中,就会得到该文件的路径,千万不要一个一个字符敲.
    可以看到,这个库文件包含armv7,arm64两个指令集的数据.

    2,编译i386和x86_64的版本

    接下来,随便选择一个模拟器,再次确认在Release模式下,cmd+B.


    然后也会在Products中生成一个framework,我们ShowInFinder,发现这次这个库文件在Release-iphonesimulator文件夹下.


    image.png

    同样,我们查看这个库文件支持的指令集:


    可以看到,这个库文件包含i386和x86_64两个指令集的数据.

    3,合并两个版本

    因为我们最终需要得到包含4个指令集数据的库文件,所以我们需要进行合并操作.

    首先,我们随便复制一整套库文件,并命名为Release-all,并将该套文件下的GaPersonFramework删掉,因为我们要合并一个完整的.
    其余的文件需要保留.


    合并两个版本的库文件,需要使用以下命令:
    lipo -create LibPath1 LibPath2 -output LibPath
    其中的LibPath1和LibPath2是前面两个部分库文件的路径,后面的LibPath,是我们合并后得到的完整的库文件的路径.

    完成之后,我们再次查看合并的库文件所包含的指令集:



    现在就支持4个指令集了.

    然后我们最终得到的库文件是GaPersonFramework.framework这个文件夹,里面包含了具备四个指令集代码的库文件,以及头文件.


    手动构建就此结束.

    5.2 大牛之自动构建

    1,新建一个Aggregate工程
    在原来的工程中,新建一个Cross-platform->Aggregate的Target.
    名字可以随便起.
    我起得名字为GaPersonFrameworkBuilder,因为这个Target的创建的目的就是为了自动构造GaPersonFramework.

    2,创建构建脚本
    GaPersonFrameworkBuilder->BuilderPhases->点击+号->New Run Script Phase

    将以下脚本代码粘贴到代码区域.
    代码的详细步骤,可以查看注释,已经很清楚了.

    # 1 文件夹设置
    # 1.1 设置要构造的Target的名称,如果和工程名称不一样,这里需要自己填写.
    FMK_NAME=${PROJECT_NAME} 
    
    # 1.2 构造后,存放库文件的文件夹.
    INSTALL_DIR=${SRCROOT}/Products
    
    # 1.3 构造后,存放Fat库文件的文件夹
    # 该文件夹存放的是我们最终需要的合并了多个指令集的Fat库文件.
    FAT_DIR=${SRCROOT}/Products/Fat
    # Fat库文件的文件夹路径
    FAT_LIB_DIR=${FAT_DIR}/${FMK_NAME}.framework
    # Fat库文件的路径
    FAT_LIB_PATH="${FAT_LIB_DIR}/${FMK_NAME}" 
    echo "FAT_LIB_PATH is ${FAT_LIB_PATH}"
    
    
    # 1.4 构造时,存放中间Thin库文件的文件夹.
    THIN_DIR=${SRCROOT}/Products/Thin
    # 真机的库文件夹路径
    DEVICE_LIB_DIR=${THIN_DIR}/Release-iphoneos/${FMK_NAME}.framework
    # 真机库文件路径
    DEVICE_LIB_PATH=${DEVICE_LIB_DIR}/${FMK_NAME}
    # 模拟器库文件夹路径
    SIMULATOR_LIB_DIR=${THIN_DIR}/Release-iphonesimulator/${FMK_NAME}.framework
    # 模拟器库文件路径
    SIMULATOR_LIB_PATH=${SIMULATOR_LIB_DIR}/${FMK_NAME}
    
    # 1.5 构造时的临时文件夹
    # Xcode构造时存放库文件的临时文件夹路径
    BUILD_DIR=${SRCROOT}/build 
    # 存放真机库文件的临时文件夹
    DEVICE_LIB_DIR_TEMP=${BUILD_DIR}/Release-iphoneos/${FMK_NAME}.framework
    # 存放模拟器库文件的临时文件夹
    SIMULATOR_LIB_DIR_TEMP=${BUILD_DIR}/Release-iphonesimulator/${FMK_NAME}.framework
    
    
    # 2 构建
    # 2.1 构建真机库文件
    xcodebuild -configuration "Release" -target "${FMK_NAME}" -sdk iphoneos clean build 
    if [ -d "${DEVICE_LIB_DIR}" ] 
    then 
    echo "History dir exists,delete it"
    rm -rf "${DEVICE_LIB_DIR}" 
    fi 
    echo "Create lib dir for device."
    mkdir -p "${DEVICE_LIB_DIR}" 
    cp -R "${DEVICE_LIB_DIR_TEMP}/" "${DEVICE_LIB_DIR}/" 
    if [ -f "${DEVICE_LIB_PATH}" ]
    then
    echo "Framework for iphone os make success!"
    else 
    echo "Framework for iphone os make failed"
    fi
    
    # 2.2 构建模拟器库文件
    xcodebuild -configuration "Release" -target "${FMK_NAME}" -sdk iphonesimulator clean build 
    if [ -d "${SIMULATOR_LIB_DIR}" ] 
    then 
    echo "History dir exists,delete it"
    rm -rf "${SIMULATOR_LIB_DIR}" 
    fi 
    echo "Create lib dir for simulator."
    mkdir -p "${SIMULATOR_LIB_DIR}" 
    cp -R "${SIMULATOR_LIB_DIR_TEMP}/" "${SIMULATOR_LIB_DIR}/"
    if [ -f "${SIMULATOR_LIB_PATH}" ]
    then
    echo "Framework for iphone simulator make success!"
    else 
    echo "Framework for iphone simulator make failed"
    fi
    
    
    # 3 合并
    # 3.1 确保Fat库文件夹是空的
    if [ -d "${FAT_LIB_DIR}" ] 
    then 
    echo "History file exists,delete it"
    rm -rf "${FAT_LIB_DIR}" 
    fi 
    echo "Create lib dir for fat."
    mkdir -p "${FAT_LIB_DIR}" 
    
    # 3.2 拷贝一个已经存在的库文件的文件夹
    echo "Copy device framewoke file"
    cp -R "${DEVICE_LIB_DIR}/" "${FAT_LIB_DIR}/" 
    
    # 3.3 将合并后的库文件放入Fat库文件夹内.
    lipo -create "${DEVICE_LIB_PATH}" "${SIMULATOR_LIB_PATH}" -output "${FAT_LIB_PATH}"
    # 4 清理
    # 删除Xcode构建临时文件夹
    rm -r "${BUILD_DIR}" 
    # 删除Thin库文件夹
    rm -r "${THIN_DIR}"
    # Finder 打开Fat库文件夹
    open "${FAT_DIR}" 
    

    3,运行脚本
    Scheme选择GaPersonFrameworkBuilder,设备选择Generic iOS Device,然后cmd+B,就会执行该脚本进行构建.

    构建完毕后,Finder会打开Fat文件夹,我们所需要的framework框架直接就在眼前了.
    其实该脚本做的事情,和第一种做法是一样的,只不过用脚本来实现了..


    6 使用库文件

    新建一个工程,名称为GaPersonProject.
    在这个工程中,我们要导入前面做好的Framework库文件,并使用该库文件.

    1,在工程中,将库文件拖入工程,然后引入头文件.
    将前面Fat文件夹得到的GaPersonFramework.framework拖动到工程中.

    在使用库文件的地方导入库的头文件.
    #import <GaPersonFramework.framework/GaPersonFramework.h>

    2,设置编译标志
    但是此时调用分类的方法会报错.
    因为在调用framework的时候,分类中定义的方法并不能直接被调用.
    Objective-C不会为每一个objc函数生成链接符号,而是会为每一个类生成链接符号。所以对于分类,那么链接器就不知道如何把原代码与category的代码实现关联起来,导致生成的对象无法响应属于分类的消息。

    解决办法:
    Linking->Setting->Other Linker Flags,添加-ObjC选项。

    加了这个参数后,链接器就会把静态库中所有的 Objective-C 类和分类都加载到最后的可执行文件中,但这些额外的代码会使目标文件变大。


    image.png

    Demo下载

    本文中的两个工程可以在此下载:
    链接: https://pan.baidu.com/s/1lnOYLq9OjSK4zwcidEff7g
    密码: mbjt

    文章推荐

    相关文章

      网友评论

          本文标题:iOS|Framework静态库打包教程 揭秘大牛和萌新的操作究

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