本文主要讲述了如何从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)等价于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文件夹下.
同样,我们查看这个库文件支持的指令集:
可以看到,这个库文件包含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 类和分类都加载到最后的可执行文件中,但这些额外的代码会使目标文件变大。
Demo下载
本文中的两个工程可以在此下载:
链接: https://pan.baidu.com/s/1lnOYLq9OjSK4zwcidEff7g
密码: mbjt